Как я могу клонировать объект JavaScript, кроме одного ключа?

У меня есть плоский объект JS:

{a: 1, b: 2, c: 3, ..., z:26}

Я хочу клонировать объект, кроме одного элемента:

{a: 1, c: 3, ..., z:26}

Как это сделать проще всего (если возможно, предпочитая использовать es6 / 7)?


person fox    schedule 09.01.2016    source источник
comment
Без изменения исходного объекта: JSON.parse (JSON.stringify ({... obj, 'key2': undefined}))   -  person infinity1975    schedule 29.01.2019
comment
Распространение объекта не требует JSON.parse и JSON.stringify для клонирования, но требует ES2018 ...   -  person FZs    schedule 27.12.2020


Ответы (26)


Если вы используете Babel, вы можете использовать следующий синтаксис, чтобы скопировать свойство b из x в переменную b, а затем скопировать остаток свойств в переменную y:

let x = {a: 1, b: 2, c: 3, z:26};
let {b, ...y} = x;

и он будет перенесен в:

"use strict";

function _objectWithoutProperties(obj, keys) {
  var target = {};
  for (var i in obj) {
    if (keys.indexOf(i) >= 0) continue;
    if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
    target[i] = obj[i];
  }
  return target;
}

var x = { a: 1, b: 2, c: 3, z: 26 };
var b = x.b;

var y = _objectWithoutProperties(x, ["b"]);
person Ilya Palkin    schedule 10.01.2016
comment
Если вы добавите в код неиспользуемые переменные, это приведет к Неиспользованной переменной 'b'. предупреждение хотя. - person Ross Allen; 02.09.2016
comment
как бы выглядел синтаксис, если бы у вас было let x = [{a: 1, b: 2, c: 3, z:26}, {a: 5, b: 6, c: 7, z:455}]; - person ke3pup; 09.09.2016
comment
вы можете использовать Array.prototype.map () для массива. let y = x.map(item => { /* remove one key from the item and return it */ }) - person Ilya Palkin; 09.09.2016
comment
@RossAllen В версии 3.15.0 был добавлен параметр ignoreRestSiblings (3 февраля 2017 г.). См. совершить c59a0ba - person Ilya Palkin; 07.03.2017
comment
@IlyaPalkin Интересно. Это кажется немного ленивым, потому что это не меняет того факта, что в области видимости есть b. - person Ross Allen; 07.03.2017
comment
Если вы получаете ошибку сборки модуля: SyntaxError: Unexpected token, вам, вероятно, нужно добавить плагин преобразования babel rest spread. См. Babeljs.io/docs/plugins/transform-object-rest-spread - person jsaven; 20.01.2018
comment
Как называется этот синтаксис? - person CodyBugstein; 18.03.2018
comment
Деструктурирующее назначение Я думаю - person Ilya Palkin; 19.03.2018
comment
Несмотря на то, что по этому поводу никогда не говорилось об обратном, и это не делает решение недействительным, возможно, стоит отметить, что такая функция выполняет неглубокую копию: если x содержит свойство объекта, и поскольку объект является ссылкой (codeburst.io/), вы можете по ошибке измените вложенное значение внутри x при изменении его в y. - person papillon; 19.02.2019
comment
Как побочный эффект того, что упомянул @RossAllen. Если вы хотите удалить одно и то же свойство из нескольких объектов, вам нужно будет использовать var. В противном случае вы получите сообщение об ошибке при повторном объявлении переменной let. (В моем случае это возникло в ответ на различное состояние и nextState минус одно свойство в componentShouldUpdate) - person Arye Eidelman; 15.08.2019
comment
если bName - это имя ключа темы - это не работает: let {[bName], ...y} = x; Я удаляю узел прямо сейчас, он не может делать то, что мы хотим !!!! 1111 (это была одна строка, одна функция) - person xakepp35; 09.09.2019
comment
Спасибо за ответ. Вам больше не нужен Babel для этого синтаксиса: caniuse.com/ - person adabru; 27.12.2019
comment
Чтобы избежать предупреждения о неиспользуемой переменной и по-прежнему использовать оператор распространения, вы можете: let y = {...x}; delete y.b; - person jWang1; 30.03.2020
comment
Я сделал let { b: _b, ...y } = x;, чтобы пропустить предупреждение о неиспользуемой переменной. - person Waseem; 20.05.2021

var clone = Object.assign({}, {a: 1, b: 2, c: 3});
delete clone.b;

или если вы принимаете свойство неопределенным:

var clone = Object.assign({}, {a: 1, b: 2, c: 3}, {b: undefined});
person madox2    schedule 09.01.2016
comment
Простое удаление свойства - ясный и простой способ сделать это. - person sshow; 06.07.2017
comment
Предостережение при удалении заключается в том, что это не неизменяемая операция. - person Javid Jamae; 25.07.2017
comment
Это именно то, что я искал, но немного более реализованный немного другим способом: var cake = {... currentCake, requestorId: undefined}; - person abelito; 19.04.2019
comment
Object.assign с указанным свойством как undefined сделано и спасет меня, спасибо! - person lesovsky; 24.11.2020

Я использую этот лайнер ESNext

const obj = { a: 1, b: 2, c: 3, d: 4 }
const clone = (({ b, c, ...o }) => o)(obj) // remove b and c
console.log(clone)

person vdegenne    schedule 23.11.2018
comment
Вместо упаковки в массив и map вы можете: (({b, c, ...others}) => ({...others}))(obj) - person bucabay; 22.12.2018
comment
@bucabay: Великолепно! Это лучший ответ из всех! Соответствует стандартам, отлично работает в Node и т. Д. Вы должны представить в качестве ответа. - person david.pfx; 21.01.2019
comment
@ david.pfx Есть обратная сторона: читабельность. Особенно если сравнивать с решениями типа _.omit(obj, ['b', 'c']). - person totymedli; 23.09.2019
comment
@totymedli: Не мной. Я в любое время возьму синтаксическую форму, часть стандартного ES6, вместо волшебной функции из соображений удобочитаемости. - person david.pfx; 24.09.2019
comment
Здесь никто не комментирует eval? - person Haroen Viaene; 31.01.2020
comment
@HaroenViaene Если выражение, используемое в вашем eval, не является чем-то, что пользователь МОЖЕТ потенциально использовать для злых умыслов, это нормально, вы должны осознавать потенциальный риск, однако вы правы › - person vdegenne; 31.01.2020
comment
Слишком легко взломать… omit({}, "}));alert('gotcha!')(({a") - person Brook Jordan; 21.07.2020
comment
@BrookJordan, зачем вам взламывать собственный код? Если часть кода не используется в пакете взаимодействия с пользователем, ничего страшного. - person vdegenne; 04.08.2020
comment
Это большое "если". Я не верю, что какой-либо из моих кодов в общей кодовой базе в какой-то момент не будет иметь пользовательского ввода текста ... Я не уверен, что даже верю, что никогда не забуду внутреннюю часть своего кода и не помещу пользовательский контент через это. - person Brook Jordan; 10.09.2020
comment
Я удалил пример общего назначения, проверьте историю редактирования, если вам интересно. Поскольку eval в некоторых случаях не рекомендуется, я не хочу вводить в заблуждение новичков в его использовании. - person vdegenne; 17.04.2021

Чтобы добавить к ответу Ильи Палкина: вы даже можете динамически удалять ключи:

const x = {a: 1, b: 2, c: 3, z:26};

const objectWithoutKey = (object, key) => {
  const {[key]: deletedKey, ...otherKeys} = object;
  return otherKeys;
}

console.log(objectWithoutKey(x, 'b')); // {a: 1, c: 3, z:26}
console.log(x); // {a: 1, b: 2, c: 3, z:26};

Демонстрация в Babel REPL

Источник:

person Paul Kögel    schedule 20.11.2016
comment
Это здорово, но есть ли способ избежать неиспользуемого var deletedKey? Не то чтобы это доставляло какие-либо проблемы, но JSHint жаловался, и это действительно кажется странным, поскольку мы его действительно не используем. - person Johnson Wong; 23.06.2017
comment
@JohnsonWong Как насчет использования _, которое разрешено для переменной, которую вы не собираетесь использовать? - person Ryan H.; 26.08.2017

Для тех, кто не может использовать ES6, вы можете использовать lodash или underscore.

_.omit(x, 'b')

Or ramda.

R.omit('b', x)
person Noel Llevares    schedule 13.01.2017
comment
не было бы логичнее использовать здесь опустить? _.omit(x, 'b') - person tibalt; 22.06.2017
comment
Спасибо @tibalt. Обновил ответ с ним. - person Noel Llevares; 23.06.2017
comment
delete более лаконичен - использование lodash, подчеркивания или ramda актуально только для проектов, которые уже используют и знают их, в противном случае это становится все менее актуальным в 2018 году и в последующий период. - person jimmont; 11.09.2018
comment
@jimmont delete уже упоминается в других ответах. Не нужно повторять ответы, не правда ли? И, конечно же, это актуально только для тех, кто уже использует lodash или ramda. И это также актуально только для тех, кто застрял в ES5 и ранее, как указано в этом ответе. - person Noel Llevares; 11.09.2018
comment
@dashmug мой предыдущий комментарий был критикой подхода (а не вашего ответа), который следует учитывать при выборе использования подхода, представленного в этом ответе. Мне нужна была бы эта информация, если бы я читал ответы, и это причина для добавления моего комментария и упоминания delete. - person jimmont; 19.09.2018

Вы можете написать для него простую вспомогательную функцию. В Lodash есть аналогичная функция с тем же именем: опустить

function omit(obj, omitKey) {
  return Object.keys(obj).reduce((result, key) => {
    if(key !== omitKey) {
       result[key] = obj[key];
    }
    return result;
  }, {});
}

omit({a: 1, b: 2, c: 3}, 'c')  // {a: 1, b: 2}

Также обратите внимание, что это быстрее, чем Object.assign, а затем удалить: http://jsperf.com/omit-key < / а>

person just-boris    schedule 10.01.2016
comment
Вместо этого я бы отфильтровал массив ключей, а затем уменьшил бы его без оператора if: Object.keys(obj).filter(key => key != omitKey).reduce((result, key) => ({...result, [key]: obj[key]}), {}); - person wensveen; 25.09.2020
comment
Используя его, поскольку он быстрее, чем Object.assign - person Lohith; 21.12.2020

Может быть, примерно так:

var copy = Object.assign({}, {a: 1, b: 2, c: 3})
delete copy.c;

Достаточно ли этого? Или действительно c невозможно скопировать?

person clean_coding    schedule 09.01.2016

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

const obj = { 1: 1, 2: 2, 3: 3, 4: 4 };
const removeMe = 1;

const { [removeMe]: removedKey, ...newObj } = obj;

removeMe имеет псевдоним removedKey и игнорируется. newObj становится { 2: 2, 3: 3, 4: 4 }. Обратите внимание, что удаленный ключ не существует, значение не было просто установлено равным undefined.

person goldins    schedule 02.10.2019

Использование деструктуризации объектов

const omit = (prop, { [prop]: _, ...rest }) => rest;
const obj = { a: 1, b: 2, c: 3 };
const objWithoutA = omit('a', obj);
console.log(objWithoutA); // {b: 2, c: 3}

person Ivan Nosov    schedule 23.03.2019
comment
Я предполагаю, что это решение предназначено для предотвращения предупреждения «Неиспользуемая переменная» в JSLint. К сожалению, использование _ не решает проблему для ESLint ... - person bert bruynooghe; 06.06.2019

Как насчет этого:

let clone = Object.assign({}, value);
delete clone.unwantedKey;
person Lars Juel Jensen    schedule 21.01.2020

Эй, похоже, вы сталкиваетесь со ссылками на проблемы, когда пытаетесь скопировать объект, а затем удаляете свойство. Где-то вам нужно назначить примитивные переменные, чтобы javascript создавал новое значение.

Простой трюк (может быть ужасным), который я использовал, был

var obj = {"key1":"value1","key2":"value2","key3":"value3"};

// assign it as a new variable for javascript to cache
var copy = JSON.stringify(obj);
// reconstitute as an object
copy = JSON.parse(copy);
// now you can safely run delete on the copy with completely new values
delete copy.key2

console.log(obj)
// output: {key1: "value1", key2: "value2", key3: "value3"}
console.log(copy)
// output: {key1: "value1", key3: "value3"}
person Chris Fust    schedule 07.06.2018
comment
Мне это действительно нравится. Мог сделать JSON.parse(JSON.stringify(Object.assign({}, obj, { key2: undefined })));. Его даже не нужно удалять, просто нужно ложное значение. - person Chad; 24.08.2018

Недавно я сделал это очень простым способом:

const obj = {a: 1, b: 2, ..., z:26};

просто используя оператор распространения, чтобы отделить нежелательное свойство:

const {b, ...rest} = obj;

... и object.assign, чтобы взять только "остальную" часть:

const newObj = Object.assign({}, {...rest});
person Pepdbm 7    schedule 27.02.2019
comment
rest уже является новым объектом - последняя строка вам не нужна. К тому же это идентично принятому решению. - person carpiediem; 08.05.2019

Вы также можете использовать оператор распространения, чтобы сделать это

const source = { a: 1, b: 2, c: 3, z: 26 }
const copy = { ...source, ...{ b: undefined } } // { a: 1, c: 3, z: 26 }
person Mickael M.    schedule 12.09.2018
comment
Выглядит супер изящно. Однако при этом ключ остается неопределенным в copy - person kano; 22.09.2018
comment
чтобы вы могли удалить неопределенные ключи, если там есть единственные. - person Victor; 27.09.2018
comment
не уверен, почему вы сделали дополнительное распространение в копии, const copy = { ...source, b: undefined } сводится к тому же самому. - person bert bruynooghe; 06.06.2019
comment
Как упоминалось ранее, Object.keys(copy) все равно вернет ['a', 'b', 'c', 'z'], что не всегда то, что вам нужно. - person kernel; 20.05.2021

Приведенные выше решения с использованием структурирования страдают от того, что у вас есть используемая переменная, что может вызвать жалобы со стороны ESLint, если вы ее используете.

Итак, вот мои решения:

const src = { a: 1, b: 2 }
const result = Object.keys(src)
  .reduce((acc, k) => k === 'b' ? acc : { ...acc, [k]: src[k] }, {})

На большинстве платформ (кроме IE, если не используется Babel) вы также можете:

const src = { a: 1, b: 2 }
const result = Object.fromEntries(
  Object.entries(src).filter(k => k !== 'b'))
person bert bruynooghe    schedule 06.06.2019

Если вы имеете дело с огромной переменной, вы не хотите копировать ее, а затем удалять, так как это было бы неэффективно.

Простой цикл for с проверкой hasOwnProperty должен работать, и он гораздо более адаптируется к будущим потребностям:

for(var key in someObject) {
        if(someObject.hasOwnProperty(key) && key != 'undesiredkey') {
                copyOfObject[key] = someObject[key];
        }
}
person HoldOffHunger    schedule 10.08.2017
comment
Решение оператора распространения делает то же самое с гораздо более приятным синтаксисом. - person david.pfx; 20.02.2019

Lodash опустить

let source = //{a: 1, b: 2, c: 3, ..., z:26}
let copySansProperty = _.omit(source, 'b');
// {a: 1, c: 3, ..., z:26}
person OscarRyz    schedule 05.10.2017
comment
да, Lodash - элегантный вариант, но я пытался добиться того же с помощью vanilla ES6 - person andreasonny83; 19.03.2019

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

const postData = {
   token: 'secret-token',
   publicKey: 'public is safe',
   somethingElse: true,
};

const a = {
   ...(({token, ...rest} = postData) => (rest))(),
}

/**
a: {
   publicKey: 'public is safe',
   somethingElse: true,
}
*/
person andreasonny83    schedule 19.03.2019

Я сделал это так, как пример из моего редуктора Redux:

 const clone = { ...state };
 delete clone[action.id];
 return clone;

Другими словами:

const clone = { ...originalObject } // note: original object is not altered
delete clone[unwantedKey]           // or use clone.unwantedKey or any other applicable syntax
return clone                        // the original object without the unwanted key
person Jonathan Tuzman    schedule 28.04.2019
comment
Похоже, много дополнительной работы по сравнению с принятым ответом 3-х летней давности (и оба варианта зависят от транспиляции для многих браузеров). - person carpiediem; 08.05.2019
comment
У меня есть аналогичный вариант использования (редуктор), поэтому я придумал хороший способ, который поддерживает динамические ключи без мутации. В основном: const { [removeMe]: removedKey, ...newObj } = obj; - см. Мой ответ на этот вопрос. - person goldins; 02.10.2019

Я не знаю точно, для чего вы хотите это использовать, поэтому я не уверен, сработает ли это для вас, но я просто сделал следующее, и это сработало для моего варианта использования:

const newObj ={...obj, [key]: undefined}
person Beto Shiver    schedule 27.12.2020

Если вы используете Ramda, вы можете использовать его опустить и clone для создания глубокого клона вашего объекта и опустите ненужные поля.

var object = {a: 1, b: 2, c: 3, y:25, z:26};
R.clone(R.omit(["z", "y"], object));
person varad11    schedule 05.02.2021

У меня был object, который я хотел удалить после использования его метода. Это то, что сработало для меня.


/* Sample data */
let items = [
  {
    name: 'John',
    doc: {
      set: async (item) => {/*...sets docs on remote server*/}
    }
  },{
    name: 'Mary',
    doc: {
      set: async (item) => {/*...sets docs on remote server*/}
    }
  },{
    name: 'Jack',
    doc: {
      set: async (item) => {/*...sets docs on remote server*/}
    }
  }
]

/* mapping to promises */
const promises = items.map(({doc, ...item}) => doc.set(item)); 
// utilized doc and utilized `rest` of the items :)


Удачи :)

person Aakash    schedule 13.07.2020

Вот мои два цента на Typescript, немного заимствованные из ответа @Pol и использующие вместо этого reduce.

function objectWithoutKey(object: object, keys: string[]) {
    return keys.reduce((previousValue, currentValue) => {
        // @ts-ignore
        const {[currentValue]: undefined, ...clean} = previousValue;
        return clean
    }, object)
}

// usage
console.log(objectWithoutKey({a: 1, b: 2, c: 3}, ['a', 'b']))
person Oscar Nevarez    schedule 16.08.2020

У меня есть один объект с именем: options с некоторыми ключами

  let options = {       
        userDn: 'somePropertyValue',
        username: 'someValue',
        userSearchBase: 'someValue',
        usernameAttribute: 'uid',
        userPassword: 'someValue'
    }

Я хочу зарегистрировать все свойство объекта excelp userPassword, потому что я что-то тестирую, я использую этот код:

console.log(Object.keys(options).map(x => x + ': ' + (x === "userPassword" ? '---' : options[x])));

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

person crazyX    schedule 22.10.2020

Решение с ключевым словом delete выдаст ошибку компиляции, если вы используете машинописный текст, потому что оно нарушает контракт объекта, который вы создали. И решение оператора распространения ES6 (const {x, ...keys} = object) может вызывать ошибку в зависимости от конфигурации линтинга, которую вы используете в своем проекте, поскольку переменная x не используется. Итак, я пришел к следующему решению:

const cloneObject = Object.entries(originalObject)
    .filter(entry => entry[0] !== 'keyToRemove')
    .reduce((acc, keyValueTuple) => ({ ...acc, [keyValueTuple[0]]: keyValueTuple[1] }), {});

Он решает проблему с помощью комбинации Object. Entries (для получения массива пар ключ / значение исходного объекта) и методы массива filter и уменьшить. Это выглядит многословно, но я думаю, что неплохо иметь решение с цепочкой в ​​одну строку.

person Matheus Baldissara    schedule 25.05.2021

Используя функцию loadash deepclone, вы можете:

const _obj = _.cloneDeep(obj);
delete _obj.key;

Сначала клонирует весь объект в новый объект, а затем удаляет желаемый ключ из клонированного объекта, чтобы не повлиять на исходный.

person MR_AMDEV    schedule 05.03.2021

person    schedule
comment
Хотя этот код может предоставить решение вопроса, лучше добавить контекст, объясняющий, почему и как он работает. Это может помочь будущим пользователям учиться и применять эти знания в собственном коде. Вы также, вероятно, получите положительные отзывы от пользователей в виде голосов "за", когда объясните код. - person borchvm; 12.05.2020