克隆除一个键之外的js对象

fox*_*fox 237 javascript ecmascript-harmony ecmascript-6 ecmascript-7

我有一个扁平的JS对象:

{a: 1, b: 2, c: 3, ..., z:26}
Run Code Online (Sandbox Code Playgroud)

我想克隆除一个元素之外的对象:

{a: 1, c: 3, ..., z:26}
Run Code Online (Sandbox Code Playgroud)

最简单的方法是什么(如果可能,最好使用es6/7)?

Ily*_*kin 349

如果使用Babel,则可以使用以下语法将属性b从x复制到变量b,然后将其余属性复制到变量y中:

let x = {a: 1, b: 2, c: 3, z:26};
let {b, ...y} = x;
Run Code Online (Sandbox Code Playgroud)

并且将transpiled到:

"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"]);
Run Code Online (Sandbox Code Playgroud)

  • 如果你在代码中找到未使用的变量,这将导致"未使用的变量'b'." 但警告. (49认同)
  • @RossAllen在v3.15.0(2017年2月3日)中添加了一个选项[`ignoreRestSiblings`](http://eslint.org/docs/rules/no-unused-vars#ignorerestsiblings).请参阅:[commit c59a0ba](https://github.com/eslint/eslint/commit/c59a0ba) (11认同)
  • 我执行了 `let { b: _b, ...y } = x;` 来跳过“未使用的变量”警告。 (7认同)
  • @IlyaPalkin有趣.虽然因为它并没有改变范围内存在"b"的事实,但感觉有点懒惰. (3认同)
  • 为了避免“未使用的变量”警告并仍然使用扩展运算符,您可以执行以下操作:`let y = {...x}; 删除 yb;` (3认同)
  • 如果你的模块构建失败:SyntaxError:意外的令牌,你可能需要添加babel rest spread transform插件.见babeljs.io/docs/plugins/transform-object-rest-spread (2认同)
  • 感谢你的回答。您不一定再需要 Babel 来使用此语法:https://caniuse.com/#feat=mdn-javascript_operators_destructuring_rest_in_objects (2认同)

mad*_*ox2 118

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

或者如果您接受未定义的财产:

var clone = Object.assign({}, {a: 1, b: 2, c: 3}, {b: undefined});
Run Code Online (Sandbox Code Playgroud)

  • 删除的警告是它不是一个不可变的操作. (17认同)
  • 简单地删除该属性是一种清晰而简单的方法. (6认同)
  • 这正是我正在寻找的,但稍微有点不同的实现方式: var cake = {...currentCake, requestorId: undefined }; (2认同)

Pau*_*gel 57

要添加到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};
Run Code Online (Sandbox Code Playgroud)

Babel REPL中的演示

资源:

  • @JohnsonWong如何使用允许用于不打算使用的变量的`_`? (4认同)
  • 这是一个很好的语法. (2认同)
  • 很好,但是有办法避免使用未使用的var DeletedKey吗?并不是说这会引起任何问题,而是会使JSHint抱怨,并且由于我们确实没有使用它,所以看起来很奇怪。 (2认同)

das*_*mug 54

对于那些不能使用ES6的人,你可以使用lodashunderscore.

_.omit(x, 'b')
Run Code Online (Sandbox Code Playgroud)

或者ramda.

R.omit('b', x)
Run Code Online (Sandbox Code Playgroud)

  • 在这里使用[省略](https://lodash.com/docs/4.17.4#omit)不合逻辑吗?`_.omit(x,'b')` (5认同)

vde*_*nne 33

我使用这种单线程ES6语法,

const obj = {a: 1, b: 2, c: 3, d: 4};
const clone = (({b, c, ...others}) => ({...others}))(obj); // remove b and c
console.log(clone);
Run Code Online (Sandbox Code Playgroud)

  • 你可以这样做,而不是包装在数组和`map`中:`(({b,c,... others})=>({... others}))(obj)` (7认同)
  • @totymedli:不是我做的。出于可读性的考虑,我会随时采用语法形式(标准 ES6 的一部分)而不是魔法函数。 (6认同)
  • 使用函数可确保变量“b”和“c”不会在当前范围内保持分配(或重新分配)。[对已接受答案的评论](/sf/ask/2428923381/ except-for-one-key#comment65929164_34710102)提到linter甚至可能如果变量未被使用,就会抱怨这一点。 (6认同)
  • 就我而言,**这应该是公认的答案**。紧凑,做到了一切,没有缺点。 (5认同)
  • @david.pfx 有一个缺点:可读性。特别是当您将它与“_.omit(obj, ['b', 'c'])”之类的解决方案进行比较时。 (3认同)
  • 这里没有人评论“eval”吗? (3认同)
  • 为什么使用函数?为什么不简单地这样做: ```const { b, c, ...clone } = obj``` ? (3认同)
  • @bucabay:太棒了!这是最好的答案!符合标准,在 Node 等中完美运行。您应该作为答案提交。 (2认同)

jus*_*ris 21

你可以为它编写一个简单的辅助函数.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}
Run Code Online (Sandbox Code Playgroud)

另请注意,它比Object.assign更快,然后删除:http://jsperf.com/omit-key

  • 我会过滤键数组,然后在没有 if 语句的情况下进行减少: `Object.keys(obj).filter(key => key != omitKey).reduce((result, key) => ({...结果,[key]: obj[key]}), {});` (3认同)

gol*_*ins 15

这是省略我认为尚未提及的动态键的选项:

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

const { [removeMe]: removedKey, ...newObj } = obj;
Run Code Online (Sandbox Code Playgroud)

removeMe别名为removedKey并被忽略。newObj变成{ 2: 2, 3: 3, 4: 4 }. 请注意,删除的键不存在,该值不只是设置为undefined


cle*_*ing 10

也许是这样的:

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

这够好吗?或者c实际上无法复制?


小智 9

使用对象分解

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}
Run Code Online (Sandbox Code Playgroud)

  • 我猜这个解决方案是为了防止 JSLint 中的“未使用变量”警告。不幸的是,使用 `_` 并不能解决 ESLint 的问题...... (2认同)

Lar*_*sen 9

这个怎么样:

let clone = Object.assign({}, value);
delete clone.unwantedKey;
Run Code Online (Sandbox Code Playgroud)


ZAF*_*AIN 8

我在这里使用对象解构。我已将密码分成变量,其余变量包含除密码之外的所有对象属性和值。注意:rest在这里不是固定关键字,您可以相应地命名它

const obj = {name:"john", password:"Abcds12@", id:"125455"}
const {password,...rest} = obj;
console.log(rest);
Run Code Online (Sandbox Code Playgroud)


小智 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"}
Run Code Online (Sandbox Code Playgroud)


小智 6

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

const obj = {a: 1, b: 2, ..., z:26};
Run Code Online (Sandbox Code Playgroud)

只需使用扩展运算符来分离不需要的属性:

const {b, ...rest} = obj;
Run Code Online (Sandbox Code Playgroud)

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

const newObj = Object.assign({}, {...rest});
Run Code Online (Sandbox Code Playgroud)

  • `rest` 已经是一个新对象——你不需要最后一行。另外,这与公认的解决方案相同。 (5认同)

小智 5

您也可以使用扩展运算符来执行此操作

const source = { a: 1, b: 2, c: 3, z: 26 }
const copy = { ...source, ...{ b: undefined } } // { a: 1, c: 3, z: 26 }
Run Code Online (Sandbox Code Playgroud)

  • 看起来超级漂亮。然而,这使密钥在`copy`中保持未定义 (2认同)
  • 不知道为什么你在副本中做了额外的传播,`const copy = { ...source, b: undefined }` 归结为完全相同。 (2认同)

and*_*y83 5

那这个呢?我从来没有发现这种模式,但我只是试图排除一个或多个属性,而不需要创建额外的对象。这似乎可以完成工作,但有一些我看不到的副作用。当然可读性不是很好。

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

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

/**
a: {
   publicKey: 'public is safe',
   somethingElse: true,
}
*/
Run Code Online (Sandbox Code Playgroud)


ber*_*ghe 5

上面使用结构化的解决方案确实会受到以下事实的影响:您有一个已使用的变量,如果您使用该变量,这可能会导致 ESLint 的投诉。

所以这是我的解决方案:

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

在大多数平台上(IE 除外,除非使用 Babel),你还可以这样做:

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