Удалить значение из объекта без изменения

102

Какой хороший и короткий способ удалить значение из объекта по определенному ключу без изменения исходного объекта?

Я бы хотел сделать что-то вроде:

let o = {firstname: 'Jane', lastname: 'Doe'};
let o2 = doSomething(o, 'lastname');
console.log(o.lastname); // 'Doe'
console.log(o2.lastname); // undefined

Я знаю, что для таких задач существует множество библиотек неизменяемости, но мне бы хотелось обойтись без библиотеки. Но для этого требовалось бы иметь простой и короткий способ, который можно было бы использовать во всем коде, без абстрагирования метода как вспомогательной функции.

Например, для добавления значения я делаю следующее:

let o2 = {...o1, age: 31};

Это довольно кратко, легко запомнить и не требует служебной функции.

Есть ли что-то подобное для удаления значения? ES6 очень приветствуется.

Большое спасибо!

аманн
источник
«Удалить значение без изменения объекта» не имеет смысла. Вы не можете удалить что-то и в то же время сохранить его нетронутым. На самом деле вы делаете частичную копию.
JJJ

Ответы:

226

Обновить:

Вы можете удалить свойство объекта с помощью сложного назначения Destructuring :

const doSomething = (obj, prop) => {
  let {[prop]: omit, ...res} = obj
  return res
}

Хотя, если имя свойства, которое вы хотите удалить, является статическим, вы можете удалить его с помощью простого однострочника:

let {lastname, ...o2} = o

Самый простой способ - это просто Или вы можете клонировать свой объект перед его изменением:

const doSomething = (obj, prop) => {
  let res = Object.assign({}, obj)
  delete res[prop]
  return res
}

В качестве альтернативы вы можете использовать omitфункцию из lodashслужебной библиотеки :

let o2 = _.omit(o, 'lastname')

Он доступен как часть пакета lodash или как отдельный пакет lodash.omit .

Леонид Бесчастный
источник
3
Это действительно самое простое решение? Например, для массивов вы можете a2 = a1.filter(el => el.id !== id)пропустить элемент в массиве по определенному идентификатору. Для объекта такого нет?
amann
1
Я согласен с @amann. Либо это лучшее решение, или ES6 действительно пропустил что - то о том , чтобы JS , используемые в более функционально. Например. Рубиkeep_if
Огюст Ридингер
1
@AugustinRiedinger на самом деле есть способ. Смотрите мое обновление.
Леонид Бесчастный
2
Обновленное решение по деструктуризации великолепно. Хотя мне это немного взорвало. Почему не const {[prop], ...rest} = objработает? Почему вам нужно присвоить извлеченное свойство новой переменной ( omitв данном случае)?
Branweb 02
1
@ Antoni4, вы можете добавить в no-unused-varsправило «пропустить» или «пропустить» , это просто регулярное выражение.
adrianmc
35

С деструктуризацией объекта ES7:

const myObject = {
  a: 1,
  b: 2,
  c: 3
};
const { a, ...noA } = myObject;
console.log(noA); // => { b: 2, c: 3 }
сенбон
источник
1
что, если aхранится в переменной const x = 'a'?
FFF
Ничего не меняет. a относится только к первому элементу массива. Это могло быть так: const { a,b, ...noA } = myObject; console.log(noA); // => {c: 3 }
senbon
1
это наиболее элегантное и простое решение
Джо
1
Есть ли способ сделать это, если клавиши числовые?
Марк Ленивец Истман,
25

однострочное решение

const removeKey = (key, {[key]: _, ...rest}) => rest;
панкста
источник
4

Как было предложено в комментариях выше, если вы хотите расширить это, чтобы удалить более одного элемента из вашего, objectмне нравится использоватьfilter . а такжеreduce

например

    const o = {
      "firstname": "Jane",
      "lastname": "Doe",
      "middlename": "Kate",
      "age": 23,
      "_id": "599ad9f8ebe5183011f70835",
      "index": 0,
      "guid": "1dbb6a4e-f82d-4e32-bb4c-15ed783c70ca",
      "isActive": true,
      "balance": "$1,510.89",
      "picture": "http://placehold.it/32x32",
      "eyeColor": "green",
      "registered": "2014-08-17T09:21:18 -10:00",
      "tags": [
        "consequat",
        "ut",
        "qui",
        "nulla",
        "do",
        "sunt",
        "anim"
      ]
    };

    const removeItems = ['balance', 'picture', 'tags']
    console.log(formatObj(o, removeItems))

    function formatObj(obj, removeItems) {
      return {
        ...Object.keys(obj)
          .filter(item => !isInArray(item, removeItems))
          .reduce((newObj, item) => {
            return {
              ...newObj, [item]: obj[item]
            }
          }, {})
      }
    }

    function isInArray(value, array) {
      return array.indexOf(value) > -1;
    }

АК85
источник
1
Почему бы вам не применить условие фильтра в сокращении, чтобы сэкономить один цикл?
Иво Сабев
спасибо за совет @IvoSabev. У меня есть несколько функций в моем коде, где я фильтрую, а затем уменьшаю, но рассмотрю ваше предложение, чтобы сохранить цикл.
ak85
4

Чтобы добавить пикантности в Performance. Проверьте эту резьбу ниже

https://github.com/googleapis/google-api-nodejs-client/issues/375

Использование оператора удаления отрицательно сказывается на производительности для шаблона скрытых классов V8. В общем, не рекомендуется его использовать.

В качестве альтернативы, чтобы удалить собственные перечислимые свойства объекта, мы могли бы создать новую копию объекта без этих свойств (пример с использованием lodash):

_.omit (o, 'prop', 'prop2')

Или даже укажите для значения свойства значение null или undefined (которое неявно игнорируется при сериализации в JSON):

o.prop = undefined

Вы также можете использовать разрушающий способ

const {remov1, remov2, ...new} = old;
old = new;

И более практичный пример:

this._volumes[this._minCandle] = undefined;
{ 
     const {[this._minCandle]: remove, ...rest} = this._volumes;
     this._volumes = rest; 
}

Как видите, вы можете использовать [somePropsVarForDynamicName]: scopeVarNameсинтаксис для динамических имен. И вы можете заключить все в скобки (новый блок), чтобы все остальное было собрано после него.

Вот тест: введите описание изображения здесь

exec:

введите описание изображения здесь

Или мы можем использовать какую-нибудь функцию вроде

function deleteProps(obj, props) {
    if (!Array.isArray(props)) props = [props];
    return Object.keys(obj).reduce((newObj, prop) => {
        if (!props.includes(prop)) {
            newObj[prop] = obj[prop];
        }
        return newObj;
    }, {});
}

для машинописного текста

function deleteProps(obj: Object, props: string[]) {
    if (!Array.isArray(props)) props = [props];
    return Object.keys(obj).reduce((newObj, prop) => {
        if (!props.includes(prop)) {
            newObj[prop] = obj[prop];
        }
        return newObj;
    }, {});
}

Применение:

let a = {propH: 'hi', propB: 'bye', propO: 'ok'};

a = deleteProps(a, 'propB'); 

// or 

a = deleteProps(a, ['propB', 'propO']);

Таким образом создается новый объект. И быстродействие объекта сохраняется. Что может быть важным или иметь значение. Если отображение и объект будут доступны много-много раз.

Также undefinedхорошим способом может быть общение . Когда вы можете себе это позволить. И для ключей вы тоже можете проверить значение. Например, чтобы получить все активные ключи, вы делаете что-то вроде:

const allActiveKeys = Object.keys(myObj).filter(k => myObj[k] !== undefined);
//or
const allActiveKeys = Object.keys(myObj).filter(k => myObj[k]); // if any false evaluated value is to be stripped.

Неопределенный не подходит для большого списка. Или разработка в течение долгого времени с множеством возможностей. Поскольку использование памяти будет продолжать расти и никогда не будет очищено. Так что это зависит от использования. И просто создание нового объекта кажется хорошим способом.

Тогда Premature optimization is the root of all evilвступит в силу. Итак, вам нужно знать о компромиссе. А что нужно, а что нет.

Замечание о _.omit () от lodash

Он удален из версии 5. Вы не можете найти его в репо. И вот вопрос, что об этом поговорим.

https://github.com/lodash/lodash/issues/2930

v8

Вы можете проверить это, и это полезно для чтения https://v8.dev/blog/fast-properties

Мохамед Аллал
источник
1

Моя проблема с принятым ответом из стандарта правил ESLint, если вы попытаетесь деструктурировать:

    const { notNeeded, alsoNotNeeded, ...rest } = { ...ogObject };

2 новые переменные, notNeededи alsoNotNeededв зависимости от вашей настройки может выдать предупреждение или ошибку, поскольку теперь они не используются. Так зачем создавать новые вары, если они не используются?

Я думаю, вам нужно по-настоящему использовать эту deleteфункцию.

Фил Лакс
источник
4
Теперь у no-unused-varsправила есть возможность ignoreRestSiblingsохватить этот вариант использования: eslint.org/docs/rules/no-unused-vars#ignorerestsiblings
amann
0

с lodash cloneDeep и удалить

(примечание: lodash clone можно использовать вместо неглубоких объектов)

const obj = {a: 1, b: 2, c: 3}
const unwantedKey = 'a'

const _ = require('lodash')
const objCopy = _.cloneDeep(obj)
delete objCopy[unwantedKey]
// objCopy = {b: 2, c: 3}
FFF
источник
0

Для моего кода мне нужна была короткая версия для возвращаемого значения map (), но решения для операций multiline / mutli были «уродливыми». Ключевой особенностью является старое void(0)решение undefined.

let o2 = {...o, age: 31, lastname: void(0)};

Свойство остается в объекте:

console.log(o2) // {firstname: "Jane", lastname: undefined, age: 31}

но структура передачи убивает меня (bc stringify):

console.log(JSON.stringify(o2)) // {"firstname":"Jane","age":31}
Джонни
источник