【问题标题】:How can I clone a JavaScript object except for one key?除了一个键,我如何克隆一个 JavaScript 对象?
【发布时间】:2016-04-14 10:13:43
【问题描述】:

我有一个扁平的 JS 对象:

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

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

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

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

【问题讨论】:

  • 不改变原始对象:JSON.parse(JSON.stringify({...obj, 'key2': undefined}))
  • 对象传播不需要JSON.parseJSON.stringify来克隆,但是需要ES2018...

标签: javascript ecmascript-6 ecmascript-harmony ecmascript-2016


【解决方案1】:

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

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

it will be 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"]);

【讨论】:

  • 如果您对代码中未使用的变量进行 lint,这将导致“未使用的变量 'b'”。不过警告。
  • @RossAllen 在 v3.15.0(2017 年 2 月 3 日)中添加了一个选项 ignoreRestSiblings。见:commit c59a0ba
  • @IlyaPalkin 很有趣。虽然感觉有点懒惰,因为它并没有改变在范围内有一个b 的事实。
  • 如果你得到 Module build failed: SyntaxError: Unexpected token,你可能需要添加 babel rest spread 转换插件。请参阅 babeljs.io/docs/plugins/transform-object-rest-spread
  • 我做了一个let { b: _b, ...y } = x; 来跳过“未使用的变量”警告。
【解决方案2】:
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});

【讨论】:

  • 简单地删除该属性是一种清晰而简单的方法。
  • 删除的警告是它不是一个不可变的操作。
  • 这正是我一直在寻找的,但实现方式略有不同:var cake = {...currentCake, requestorId: undefined };
  • Object.assign 指定的属性为 undefined 并保存我的一天,谢谢!
  • @JavidJamae 回到每个人都痴迷于一切事物不变性的时代
【解决方案3】:

我用这个 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)

【讨论】:

  • 你可以这样做:(({b, c, ...others}) => ({...others}))(obj)
  • @bucabay:太棒了!这是很多人的最佳答案!符合标准,在 Node 等中完美运行。您应该提交作为答案。
  • @david.pfx 有一个缺点:可读性。尤其是当您将其与 _.omit(obj, ['b', 'c']) 等解决方案进行比较时。
  • @totymedli:不是我。出于可读性考虑,我将随时采用标准 ES6 的语法形式,而不是魔术函数。
  • 这里没有人评论eval
【解决方案4】:

不能使用ES6的可以使用lodash或者underscore

_.omit(x, 'b')

ramda

R.omit('b', x)

【讨论】:

  • 在这里使用omit 不是更合乎逻辑吗? _.omit(x, 'b')
  • 谢谢@tibalt。用它更新了答案。
  • delete 更简洁——使用 lodash、underscore 或 ramda 只与已经使用并了解它们的项目相关,否则这在 2018 年及以后越来越无关紧要。
  • @jimmont delete 已在其他答案中提及。不需要重复的答案,你不觉得吗?当然,它只与那些已经使用 lodash 或 ramda 的人有关。而且它也只与那些坚持使用 ES5 及更早版本的人有关,如本答案所述。
  • @dashmug 我之前的评论是对选择使用此答案中表示的方法时应注意的方法(不是您的答案)的批评。如果我正在阅读答案,我会想要这些信息,这是添加我的评论并提及 delete 的原因。
【解决方案5】:

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

Demo in Babel REPL

来源:

【讨论】:

  • 这很好,但是有没有办法避免未使用的 var deletedKey?并不是说它会导致任何问题,而是让 JSHint 抱怨,而且看起来很奇怪,因为我们真的没有使用它。
  • @JohnsonWong 如何使用_,它允许用于您不打算使用的变量?
  • var b = {a:44, b:7, c:1}; let {['a']:z, ...others} = b; console.log(z , others ); // logs: 44, {b:7, c:1}
【解决方案6】:

你可以为它写一个简单的辅助函数。 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 和 delete 快:http://jsperf.com/omit-key

【讨论】:

  • 我会改为过滤键数组,然后在没有 if 语句的情况下减少:Object.keys(obj).filter(key => key != omitKey).reduce((result, key) => ({...result, [key]: obj[key]}), {});
  • 使用它,因为它比 Object.assign 快
【解决方案7】:

这里有一个选项可以省略我认为尚未提及的动态键:

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

【讨论】:

    【解决方案8】:

    可能是这样的:

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

    这够好吗?或者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}

      【讨论】:

      • 我猜这个解决方案是为了防止 JSLint 中的“未使用变量”警告。不幸的是,使用_ 并不能解决 ESLint 的问题...
      • 你需要的eslint规则是"no-unused-vars": ["error", { "args": "all", "argsIgnorePattern": "^_" } ]
      【解决方案10】:

      这个怎么样:

      let clone = Object.assign({}, value);
      delete clone.unwantedKey;
      

      【讨论】:

        【解决方案11】:

        嘿,当您尝试复制对象然后删除属性时,您似乎遇到了引用问题。您必须在某个地方分配原始变量,以便 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 })));。甚至不必删除它,只需要一个虚假值。
        【解决方案12】:

        您也可以使用扩展运算符来做到这一点

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

        【讨论】:

        • 看起来超级漂亮。但是,这会使密钥在 copy 中保持未定义
        • 所以你可以删除未定义的键,如果有唯一的键在里面
        • 不知道你为什么要在副本中进行额外的传播,const copy = { ...source, b: undefined } 归结为完全相同。
        • 如前所述,Object.keys(copy) 仍会返回 ['a', 'b', 'c', 'z'],这并不总是您想要的。
        【解决方案13】:

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

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

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

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

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

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

        【讨论】:

        • rest 已经是一个新对象 - 您不需要最后一行。另外,这与公认的解决方案相同。
        • 如果我想删除多个属性怎么办?
        【解决方案14】:

        我是这样完成的,以我的 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 年前公认的答案相比,似乎需要做很多额外的工作(而且这两个选项都依赖于对许多浏览器的编译)。
        • 我有一个类似的用例(reducer),所以我想出了一个很好的方法来支持动态键而不会发生突变。基本上:const { [removeMe]: removedKey, ...newObj } = obj; - 请参阅我对这个问题的回答。
        【解决方案15】:

        上述使用结构化的解决方案确实会受到这样一个事实的影响,即您有一个已使用的变量,如果您使用它可能会引起 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'))
        

        【讨论】:

          【解决方案16】:

          如果您正在处理一个巨大的变量,您不想复制它然后删除它,因为这样会效率低下。

          一个带有 hasOwnProperty 检查的简单 for 循环应该可以工作,而且它更能适应未来的需求:

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

          【讨论】:

          • 展开运算符解决方案的作用完全相同,但语法要好得多。
          【解决方案17】:

          洛达什omit

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

          【讨论】:

          • 是的,Lodash 是一个优雅的选择,但我试图用 vanilla ES6 达到同样的效果
          【解决方案18】:

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

          const postData = {
             token: 'secret-token',
             publicKey: 'public is safe',
             somethingElse: true,
          };
          
          const a = {
             ...(({token, ...rest} = postData) => (rest))(),
          }
          
          /**
          a: {
             publicKey: 'public is safe',
             somethingElse: true,
          }
          */
          

          【讨论】:

            【解决方案19】:
            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'));
            

            【讨论】:

            • 虽然此代码可能会为问题提供解决方案,但最好添加有关其工作原理/方式的上下文。这可以帮助未来的用户学习并将这些知识应用到他们自己的代码中。在解释代码时,您也可能会以赞成票的形式从用户那里获得积极的反馈。
            【解决方案20】:

            如果您使用Ramda,您可以使用其omitclone 函数对您的对象进行深度克隆并省略不必要的字段。

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

            【讨论】:

              【解决方案21】:

              我不确切知道你想用这个做什么,所以我不确定这是否适合你,但我只是做了以下,它适用于我的用例:

              const newObj ={...obj, [key]: undefined}
              

              【讨论】:

              • 这样您将获得具有[key] 属性和value = undefined 的对象。这与没有 [key] 属性的对象不同。例如。 Object.keys() 会给你在这些对象上不同的结果。
              【解决方案22】:

              如果您使用 typescript,删除关键字解决方案将引发编译错误,因为它违反了您实例化对象的约定。并且 ES6 扩展运算符解决方案 (const {x, ...keys} = object) 可能会根据您在项目中使用的 linting 配置引发错误,因为未使用 x 变量。所以我想出了这个解决方案:

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

              它使用Object.entries 方法(获取原始对象的键/值对数组)和数组方法filterreduce 的组合来解决问题。它看起来很冗长,但我认为有一个单行可链接的解决方案很好。

              【讨论】:

                【解决方案23】:

                我有一个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 :)
                
                
                

                祝你好运:)

                【讨论】:

                  【解决方案24】:

                  这是我在 Typescript 上的两分钱,略微源自@Paul 的回答,并改用 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']))
                  

                  【讨论】:

                    【解决方案25】:

                    我有一个名为: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,您可以放置​​要排除的属性的值

                    【讨论】:

                      【解决方案26】:

                      使用 lodash 快速清洁,使用此解决方案,您可以删除多个键,并且无需更改原始对象。这在不同情况下更具可扩展性和可用性:

                      import * as _ from 'lodash';
                      
                      function cloneAndRemove(
                          removeTheseKeys: string[],
                          cloneObject: any,
                      ): object | never {
                          const temp = _.cloneDeep(cloneObject);
                          removeTheseKeys.forEach((key) => {
                              delete temp[key];
                          });
                      
                          return temp;
                      }
                      
                      export { cloneAndRemove };
                      

                      【讨论】:

                        【解决方案27】:

                        这是一个 Typescript 方法来做到这一点。需要 2 个参数,对象和包含要删除的键的字符串数组:

                        removeKeys(object: { [key: string]: any }, keys: string[]): { [key: string]: any } {
                          const ret: { [key: string]: any } = {};
                        
                          for (const key in object) {
                            if (!keys.includes(key)) {
                              ret[key] = object[key];
                            }
                          }
                        
                          return ret;
                        }
                        

                        【讨论】:

                          【解决方案28】:

                          使用 loadash deepclone 功能可以做到:

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

                          首先将整个对象克隆为新对象,然后从克隆对象中删除所需的键,以免影响原始对象。

                          【讨论】:

                            猜你喜欢
                            • 2011-12-19
                            • 2011-04-15
                            • 2011-01-16
                            • 1970-01-01
                            • 1970-01-01
                            • 2013-10-11
                            • 2015-04-25
                            • 1970-01-01
                            相关资源
                            最近更新 更多