Видалити значення з об’єкта без мутації


101

Який хороший і короткий спосіб видалити значення з об’єкта за певним ключем без мутації вихідного об’єкта?

Я хотів би зробити щось на зразок:

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

Відповіді:


224

Оновлення:

Ви можете видалити властивість з об’єкта за допомогою хитрого призначення деструктуризації :

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 насправді, є спосіб. Дивіться моє оновлення.
Леонід Бесчасний

1
Оновлене рішення щодо деструктуризації є чудовим. Хоча мій розум від цього трохи вражений. Чому не вийде const {[prop], ...rest} = obj? Чому ви повинні призначити витягнуте властивість новій змінній ( omitу цьому випадку)?
branweb

1
@ Antoni4 ви можете додати до свого no-unused-varsправила "пропустити" або "пропустити" , це просто регулярний вираз.
adrianmc

33

З деструктуризацією об'єктів 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 }
сенбон

1
це найбільш елегантне, просте рішення
Джо

1
Чи є спосіб зробити це, якщо клавіші номери?
Марк Лень Істман,


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;
    }


1
Чому б вам не застосувати умову фільтра при зменшенні, щоб ви могли заощадити один цикл?
Іво Сабєв

дякую за підказку @IvoSabev. У моєму коді є кілька функцій, де я фільтрую, а потім зменшую, але буду розглядати вашу пропозицію щодо збереження циклу.
ak85

4

Щоб додати трохи спецій, привносячи продуктивність. Перевірте цю нитку нижче

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

Використання оператора видалення негативно впливає на ефективність для шаблону прихованих класів V8. Загалом рекомендується не використовувати його.

Крім того, щоб видалити властивості об’єкта, що перелічуються, ми могли б створити нову копію об’єкта без цих властивостей (приклад використання lodash):

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

Або навіть визначити значення властивості як null або undefined (що неявно ігнорується при серіалізації до JSON):

o.prop = невизначений

Ви можете використовувати занадто руйнівний спосіб

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 можна використовувати замість дрібних предметів)

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}

0

Для свого коду я хотів коротку версію для поверненого значення map (), але рішення для багаторядкових / мульти-операцій були "потворними". Ключовою особливістю є старий, void(0)який вирішує undefined.

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

Власність залишається в об'єкті:

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

але фреймворк передачі вбиває це для мене (до н.е. stringify):

console.log(JSON.stringify(o2)) // {"firstname":"Jane","age":31}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.