Як я можу клонувати об’єкт JavaScript, окрім одного ключа?


308

У мене плоский об'єкт JS:

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

Я хочу клонувати об’єкт за винятком одного елемента:

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

Який найпростіший спосіб зробити це (вважаючи за краще використовувати es6 / 7, якщо можливо)?


Без зміни оригінального об’єкта: JSON.parse (JSON.stringify ({... obj, 'key2': undefined}))
infinity1975

Відповіді:


441

Якщо ви використовуєте 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"]);

58
Якщо ви залишите свій код на невикористані змінні, це призведе до "Невикористаної змінної 'b". застереження, хоча.
Росс Аллен

1
як би виглядав синтаксис, якби у васlet x = [{a: 1, b: 2, c: 3, z:26}, {a: 5, b: 6, c: 7, z:455}];
ke3pup

14
@RossAllen Існує опція, ignoreRestSiblingsяка була додана в v3.15.0 (3 лютого 2017 року). Дивіться: commit c59a0ba
Ілля Палкін

4
@IlyaPalkin Цікаво. Це трохи ліниво, тому що це не змінює факту, що є bсфера застосування.
Росс Аллен

2
Якщо збірка модуля не вдалася: SyntaxError: Неочікуваний маркер, вам, ймовірно, потрібно додати плагін перетворення трансформації розсилки вавило. Дивіться babeljs.io/docs/plugins/transform-object-rest-spread
jsaven

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

7
Просто видалення властивості - це зрозумілий і простий спосіб зробити це.
sshow

24
Застереження з видаленням полягає в тому, що це не незмінна операція.
Джавід Джамай

1
це зберігає ключ
Fareed Alnamrouti

1
Мій коментар був написаний ще до того, як він
змінив

Це саме те, що я шукав, але трохи більше реалізований дещо по-іншому: var cake = {... currentCake, requestorId: undefined};
abelito

73

Щоб додати відповідь Іллі Палкіна: ви навіть можете динамічно видаляти ключі:

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

Демо в Вавілонській РЕПЛ

Джерело:


3
Це приємний синтаксис.
Гінріх

2
Це чудово, але чи є спосіб уникнути невикористаного вара видаленого ключа? Не те, що це викликає будь-які проблеми, але змушує JSHint скаржитися, і здається дивним, оскільки ми насправді не використовуємо це.
Джонсон Вонг

6
@JohnsonWong Як ​​щодо використання _дозволеної змінної, яку ви не збираєтесь використовувати?
Райан Х.

var b = {a:44, b:7, c:1}; let {['a']:z, ...others} = b; console.log(z , others ); // logs: 44, {b:7, c:1}
jimmont

70

Для тих, хто не може використовувати ES6, ви можете використовувати lodashабо underscore.

_.omit(x, 'b')

Або ramda.

R.omit('b', x)

6
чи не було б більш логічним тут використання пропуску ? _.omit(x, 'b')
tibalt

Дякую @tibalt. Оновлено відповідь із нею.
Ноель Ллеварес

delete є більш стислим - використання lodash, підкреслення або ramda стосується лише проектів, які вже їх використовують та знають, інакше це все більше не має значення у 2018 році та за його межами.
jimmont

1
@jimmont delete вже згадується в інших відповідях. Не потрібно дублювати відповіді, не думаєте? І звичайно, це стосується лише тих, хто вже використовує лодаш або рамду. І це стосується лише тих, хто застряг із ES5 та раніше, як зазначено у цій відповіді.
Ноель Ллеварес

@dashmug мій попередній коментар був критикою підходу (не вашої відповіді), який слід зазначити при виборі використовувати підхід, представлений у цій відповіді. Я хотів би отримати цю інформацію, якби я читав відповіді, і це було причиною для додавання мого коментаря та згадки delete.
Джиммонт

63

Я використовую цей 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)


Якщо вам потрібна функція загального призначення:

function omit(obj, props) {
  props = props instanceof Array ? props : [props]
  return eval(`(({${props.join(',')}, ...o}) => o)(obj)`)
}

// usage
const obj = { a: 1, b: 2, c: 3, d: 4 }
const clone = omit(obj, ['b', 'c'])
console.log(clone)


9
Замість того, щоб загортати масив, mapви можете зробити:(({b, c, ...others}) => ({...others}))(obj)
bucabay

@bucabay: Блискуче! Це найкраща відповідь партії! Стандарти сумісні, прекрасно працюють у вузлі тощо. Ви повинні подати як відповідь.
david.pfx

3
Фантастична відповідь товариш .. <3
Ajithkumar S

5
@totymedli: Не мені. Я буду приймати синтаксичну форму, що є частиною стандартного ES6, над магічною функцією в будь-який час на основі читабельності.
david.pfx

1
Блискуча. Це все.
darksoulsong

22

Ви можете написати просту функцію помічника для цього. Лодаш має аналогічну функцію з однойменною назвою: опустити

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


11

Можливо, щось подібне:

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

Це досить добре? Або cнасправді не можна скопіювати?


11

Використання об'єктної деструктуризації

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}


Чудове рішення!
Денис

2
Я думаю, що це рішення призначене для запобігання попередження "Невикористана змінна" в JSLint. На жаль, використання _не вирішує проблему для ESLint ...
bert bruynooghe

6

Здається, вам здається, що ви намагаєтеся скопіювати проблеми, коли ви намагаєтесь скопіювати об’єкт, а потім видалити властивість. Десь потрібно призначити примітивні змінні, щоб 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"}

Мені це справді подобається. Міг би зробити JSON.parse(JSON.stringify(Object.assign({}, obj, { key2: undefined })));. Не потрібно навіть видаляти його, просто потрібне хибне значення.
Чад

6

Ось варіант пропускання динамічних клавіш, які, на мою думку, ще не згадані:

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.


Приємне рішення. У мене був подібний випадок використання (динамічний ключ у редукторі).
ericgio

4

НАЙБІЛЬШИЙ ШЛЯХ

const allAlphabets = {a: 1, b: 2, c: 3, ..., z:26};
const { b, ...allExceptOne } = allAlphabets;
console.log(allExceptOne);   // {a: 1, c: 3, ..., z:26}

3

Лодаш опустіть

let source = //{a: 1, b: 2, c: 3, ..., z:26}
let copySansProperty = _.omit(source, 'b');
// {a: 1, c: 3, ..., z:26}

да, Lodash це елегантний варіант, але я намагався домогтися того ж з ванільним ES6
andreasonny83

3

Для цього також можна використовувати оператор розповсюдження

const source = { a: 1, b: 2, c: 3, z: 26 }
const copy = { ...source, ...{ b: undefined } } // { a: 1, c: 3, z: 26 }

2
Здається супер витонченим. Це, однак, зберігає ключ як невизначенийcopy
кано

тож ви можете видалити невизначені ключі, якщо там будуть лише ті, хто там
Віктор,

1
не впевнений, чому ви зробили додаткове поширення в копії, const copy = { ...source, b: undefined } зводиться до точно такого ж.
bert bruynooghe

3

Наведені вище рішення щодо використання структуризації страждають від того, що у вас є використана змінна, яка може викликати скарги 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'))


2

Якщо ви маєте справу з величезною змінною, ви не хочете її копіювати та видаляти, оскільки це буде неефективно.

Простий цикл для перевірки hasOwnProperty повинен працювати, і він набагато більш пристосований до майбутніх потреб:

for(var key in someObject) {
        if(someObject.hasOwnProperty(key) && key != 'undesiredkey') {
                copyOfObject[key] = someObject[key];
        }
}

1
Рішення оператора розповсюдження робить саме те саме з набагато приємнішим синтаксисом.
david.pfx

2

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

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

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

/**
a: {
   publicKey: 'public is safe',
   somethingElse: true,
}
*/

2

Я зробив це таким чином, як приклад з мого редуктора 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

Схоже, багато зайвої роботи порівняно з прийнятою відповіддю 3 роки тому (і обидва варіанти залежать від трансляції для багатьох браузерів).
carpiediem

У мене є подібний випадок використання (редуктор), тому я придумав чудовий спосіб, який підтримує динамічні клавіші без мутації. В основному: const { [removeMe]: removedKey, ...newObj } = obj;- дивіться мою відповідь на це питання.
goldins

1

Нещодавно я зробив це дуже простим способом:

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

просто використовуючи оператор розповсюдження, щоб розділити небажану властивість:

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

... і object.assign взяти лише частину 'rest':

const newObj = Object.assign({}, {...rest});

3
restце вже новий об'єкт - останній рядок вам не потрібен. Плюс це ідентично прийнятому рішенню.
carpiediem

0
const x = {obj1: 1, pass: 2, obj2: 3, obj3:26};

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

console.log(objectWithoutKey(x, 'pass'));

1
Хоча цей код може вирішити питання, краще додати контекст, чому / як він працює. Це може допомогти майбутнім користувачам навчитися та застосувати ці знання до власного коду. Ви також можете мати позитивні відгуки від користувачів у вигляді оновлень, коли пояснюється код.
borchvm
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.