如何克隆一个键以外的JavaScript对象?


307

我有一个平面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

Answers:


440

如果使用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 ignoreRestSiblingsv3.15.0(2017年2月3日)中添加了一个选项。参见:commit c59a0ba
Ilya Palkin's

4
@IlyaPalkin有趣。不过,这有点懒惰,因为它不会改变b范围内的事实。
罗斯·艾伦

2
如果模块构建失败:SyntaxError:意外令牌,则可能需要添加babel rest传播转换插件。参见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
我的评论写在他编辑答案并添加删除声明之前
Fareed Alnamrouti

这正是我在寻找的东西,但是以略有不同的方式实现了一些:var cake = {... currentCake,requestorId:undefined};
abelito

73

要增加Ilya Palkin的答案:您甚至可以动态删除键:

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中的演示

资源:


3
这是一个很好的语法。
辛里奇

2
很好,但是有办法避免使用未使用的var DeletedKey吗?并不是说这会引起任何问题,而是会让JSHint抱怨,而且由于我们实际上并未使用它,因此看起来很奇怪。
Johnson Wong

6
@JohnsonWong如何使用不想使用_的变量?
Ryan H.

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的用户,可以使用lodashunderscore

_.omit(x, 'b')

或者ramda

R.omit('b', x)

6
在这里使用省略会更合乎逻辑吗? _.omit(x, 'b')
tibalt

谢谢@tibalt。用它更新了答案。
Noel Llevares

删除更简洁-使用lodash,下划线或ramda仅与已经使用并了解它们的项目相关,否则在2018年及以后不再相关。
jimmont

1
@jimmont delete在其他答案中已经提到。不需要重复的答案,您不觉得吗?当然,它仅与已经使用lodash或ramda的用户有关。而且,它也仅与本答复中所述的那些使用ES5和更早版本的用户有关。
Noel Llevares 18/09/10

@dashmug我之前的评论是对方法(而非您的答案)的批评,在选择使用此答案中表示的方法时应注意。如果我通读了答案,那么我将需要此信息,这也是添加我的评论和提及的原因delete
jimmont

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:太棒了!那是最好的答案!符合标准,可以在Node等中完美运行。您应该作为答案提交。
david.pfx


5
@totymedli:不是我的。基于可读性,我将随时通过魔术函数采用标准ES6的语法形式。
david.pfx

1
辉煌。就这样。
darksoulsong

22

您可以为此编写一个简单的辅助函数。Lodash具有类似的功能,但名称相同:omit

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


不错的解决方案。我有一个类似的用例(Reducer中的动态键)。
ericgio '19

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

您还可以使用传播算子来执行此操作

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

2
似乎超级漂亮。但是,这使密钥保持copy
不变

因此,如果只有唯一的键,您可以删除未定义的键
Victor

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] }, {})

在大多数平台上(除非使用Babel,否则IE除外),您还可以执行以下操作:

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


2

如果您要处理的是巨大的变量,则不希望先复制然后删除它,因为这样效率低下。

一个具有hasOwnProperty检查的简单for循环应该可以工作,并且更适合将来的需求:

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 reducer中的示例:

 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;-看到我对这个问题的回答。
金币

1

我最近以非常简单的方式做到了:

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

只是使用传播运算符来分隔不需要的属性:

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

...和object.assign只占“其余”部分:

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.