【问题标题】:How to get a subset of a javascript object's properties如何获取 javascript 对象属性的子集
【发布时间】:2020-01-12 06:41:16
【问题描述】:

假设我有一个对象:

elmo = { 
  color: 'red',
  annoying: true,
  height: 'unknown',
  meta: { one: '1', two: '2'}
};

我想创建一个具有其属性子集的新对象。

 // pseudo code
 subset = elmo.slice('color', 'height')

 //=> { color: 'red', height: 'unknown' }

我怎样才能做到这一点?

【问题讨论】:

标签: javascript object


【解决方案1】:

两种常见的方法是 destructuring 和传统的类似 Lodash 的 pick/omit 实现。它们之间的主要实际区别在于,解构需要一个键列表是静态的,不能省略它们,包括不存在的选择键,即它是包容性的。这可能是可取的,也可能是不可取的,并且不能针对解构语法进行更改。

给定:

var obj = { 'foo-bar': 1, bar: 2, qux: 3 };

常规选择foo-bar、bar、baz键的预期结果:

{ 'foo-bar': 1, bar: 2 }

inclusive 采摘的预期结果:

{ 'foo-bar': 1, bar: 2, baz: undefined }

解构

解构语法允许使用函数参数或变量对对象进行解构和重组。

限制是键列表是预定义的,它们不能作为字符串列出,如问题中所述。如果键是非字母数字的,解构会变得更加复杂,例如foo-bar.

好处是它是 ES6 自然的高性能解决方案。

缺点是键列表是重复的,如果列表很长,这会导致代码冗长。由于在这种情况下解构复制了对象文字语法,因此可以按原样复制和粘贴列表。

IIFE

let subset = (({ 'foo-bar': foo, bar, baz }) => ({ 'foo-bar': foo, bar, baz }))(obj);

临时变量

let { 'foo-bar': foo, bar, baz } = obj;
let subset = { 'foo-bar': foo, bar, baz };

字符串列表

根据问题的需要,所选键的任意列表由字符串组成。这允许不预定义它们并使用包含键名的变量,['foo-bar', someKey, ...moreKeys]。

ECMAScript 2017 有 Object.entries 和 Array.prototype.includes,ECMAScript 2019 有 Object.fromEntries,它们可以在需要时进行 polyfill。

单线

考虑到要选择的对象包含额外的键,通常从列表中迭代键而不是对象键更有效,如果需要省略键,反之亦然。

选择(ES5)

var subset = ['foo-bar', 'bar', 'baz']
.reduce(function (obj2, key) {
  if (key in obj) // line can be removed to make it inclusive
    obj2[key] = obj[key];
  return obj2;
}, {});

省略(ES5)

var subset = Object.keys(obj)
.filter(function (key) { 
  return ['baz', 'qux'].indexOf(key) < 0;
})
.reduce(function (obj2, key) {
  obj2[key] = obj[key];
  return obj2;
}, {});

选择(ES6)

const subset = ['foo-bar', 'bar', 'baz']
.filter(key => key in obj) // line can be removed to make it inclusive
.reduce((obj2, key) => (obj2[key] = obj[key], obj2));

省略(ES6)

const subset = Object.keys(obj)
.filter(key => ['baz', 'qux'].indexOf(key) < 0)
.reduce((obj2, key) => (obj2[key] = obj[key], obj2));

选择(ES2019)

const subset = Object.fromEntries(
  ['foo-bar', 'bar', 'baz']
  .filter(key => key in obj) // line can be removed to make it inclusive
  .map(key => [key, obj[key]])
);

省略(ES2019)

const subset = Object.fromEntries(
  Object.entries(obj)
  .filter(([key]) => !['baz', 'qux'].includes(key))
);

可重用函数

单行可以表示为类似于 Lodash pick 或 omit 的可重用辅助函数,其中键列表通过参数传递,pick(obj, 'foo-bar', 'bar', 'baz')。

const pick = (obj, ...keys) => Object.fromEntries(
  keys
  .filter(key => key in obj)
  .map(key => [key, obj[key]])
);

const inclusivePick = (obj, ...keys) => Object.fromEntries(
  keys.map(key => [key, obj[key]])
);

const omit = (obj, ...keys) => Object.fromEntries(
  Object.entries(obj)
  .filter(([key]) => !keys.includes(key))
);

【讨论】:

  • 这个答案如此新近,因此没有得到应有的曝光,真是太可惜了。 IMO 它应该是完整性、简单性、多功能性和实用性的公认答案。我会将 ES6 版本保留在我最有用的 sn-p 库中。
  • 我不是 .indexOf/.includes 解决方案的粉丝 - 这是在每次迭代时进行 O(keys) 查找 = O(entries*keys)。最好翻转逻辑并仅迭代键,然后您会得到 O(keys) 总数。
  • @mpen 这个问题可以被认为是过早的优化,因为在大多数实际情况下它根本不会影响性能,所以只需选择一个易于消化的。对于适时的优化,人们可能会发现 Lodash 并没有那么快(实际上不是),并且使用数组方法迭代对象也不应该是首选。无论如何,我通常会发现自己在列表上使用迭代来选择和迭代对象键以进行省略,并更新帖子以反映这一点。
  • @EstusFlask 这可能为时过早,但是当有更快的 big-O sol'n 需要相同的行数来实现时,我更喜欢这样。如果你内联这段代码可能没问题,但是一旦你把它变成一个实用函数,它应该在 IMO 进行优化,因为你不知道它会在哪里使用。
  • @mpen 顺便说一句,它也可以相反,在一个对象中缺少很多列出的键,在这种情况下迭代一个列表将更加冗余,所以它总是取决于。
【解决方案2】:

扩展内置原型有其优势。如果您使用Object.defineProperty,您将不会产生任何污染 - 唯一剩下的问题是与未来的属性冲突(例如,您定义了Object.prototype.slice,并且将来 ES 标准将 Object.prototype.slice 定义为具有不同的功能 - 现在您的代码正在破坏应该存在于Object.prototype.slice 的预期功能。

还有…………………… ... .. ..... Elmo 的身高isn't unknown(!!!)

Object.defineProperty(Object.prototype, 'slice', {
  enumerable: false,
  writable: true,
  value: function(...args) {
    let o = {};
    for (let k of args) this.hasOwnProperty(k) && (o[k] = this[k]);
    return o;
  }
});

elmo = { 
  color: 'red',
  annoying: true,
  height: '24in',
  meta: { one: '1', two: '2'}
};

console.log(elmo.slice('color', 'height'));

【讨论】:

    【解决方案3】:

    我知道这不是最干净的,但它简单易懂。

    function obj_multi_select(obj, keys){
        let return_obj = {};
        for (let k = 0; k < keys.length; k++){
            return_obj[keys[k]] = obj[keys[k]];
        };
        return return_obj;
    };
    

    【讨论】:

    • 更容易遍历键而不是索引:for (let key of keys) return_obj[key] = obj[key];。对于 javascript,snake_case 也是非常规的
    【解决方案4】:

    像这个线程上的几个一样,我同意 evert 最明显的老式方法实际上是最好的,但是为了好玩,让我提供另一种在某些情况下不建议的方法,比如当你已经有您的子集已定义,并且您希望将属性从另一个包含超集或相交集属性的对象复制到它。

    let set = { a : 1, b : 2, c : 3 };
    let subset = { a : null, b : null };
    try {
      Object.assign(Object.seal(subset), set);
    } catch (e) {
      console.log('its ok I meant to do that <(^.^)^');
    }
    console.log(subset);

    【讨论】:

    • 这和宣传的一样~fun~ 如果不是更多
    【解决方案5】:

    我发现的最简单的方法,它不会创建不必要的变量,是一个您可以调用并且与 lodash 相同的函数,如下所示:

    pick(obj, keys){
        return  Object.assign({}, ...keys.map(key => ({ [key]: obj[key] })))
    }
    

    例如:

    pick(obj, keys){
        return  Object.assign({}, ...keys.map(key => ({ [key]: obj[key] })))
    }
    const obj = {a:1, b:2, c:3, d:4}
    const keys = ['a', 'c', 'f']
    const picked = pick(obj,keys)
    console.log(picked)
    

    pick = (obj, keys) => {
      return Object.assign({}, ...keys.map(key => ({
        [key]: obj[key]
      })))
    }
    
    const obj = {
      a: 1,
      b: 2,
      c: 3,
      d: 4
    }
    const keys = ['a', 'c', 'f']
    
    const picked = pick(obj, keys)
    console.log(picked)

    【讨论】:

      【解决方案6】:

      对象数组

      const aListOfObjects = [{
          prop1: 50,
          prop2: "Nothing",
          prop3: "hello",
          prop4: "What's up",
        },
        {
          prop1: 88,
          prop2: "Whatever",
          prop3: "world",
          prop4: "You get it",
        },
      ]
      

      可以通过这种方式解构对象来创建一个或多个对象的子集。

      const sections = aListOfObjects.map(({prop1, prop2}) => ({prop1, prop2}));
      

      【讨论】:

        【解决方案7】:

        要添加另一种深奥的方式,这也可以:

        var obj = {a: 1, b:2, c:3}
        var newobj = {a,c}=obj && {a,c}
        // {a: 1, c:3}
        

        但你必须写两次道具名称。

        【讨论】:

          【解决方案8】:

          使用带有速记对象文字语法的“with”语句

          目前还没有人演示过这种方法,可能是因为它很糟糕,你不应该这样做,但我觉得它必须列出。

          var o = {a:1,b:2,c:3,d:4,e:4,f:5}
          with(o){
            var output =  {a,b,f}
          }
          console.log(output)

          专业人士:您不必输入两次属性名称。

          缺点:不推荐使用“with”语句,原因有很多。

          结论:效果很好,但不要用。

          【讨论】:

          • 为什么不用它呢?所有这些对with 的仇恨有点过分了。有时它是适合这项工作的工具,可能包括这个工具,另一个是在渲染模板时,其中 all 它的值来自this 或其他对象。不相信我?问问 jQuery 的作者 John Resig。
          • @Dexygen,我链接到的页面列出了不使用with 语句的三个原因。不要误会我的意思,我认为它很强大并且有它的用途。实际上,我已经看到了一些问题,如果不使用它,您似乎无法解决它们。让我换个说法,不要随便使用它。这有点像eval,每个人都说它不好,你不应该使用它,但事实并非如此,有些地方eval 需要使用,但它应该始终与深思熟虑,确保添加安全漏洞或削弱代码优化。
          • 不*添加安全漏洞...
          【解决方案9】:

          TypeScript 解决方案:

          function pick<T extends object, U extends keyof T>(
            obj: T,
            paths: Array<U>
          ): Pick<T, U> {
            const ret = Object.create(null);
            for (const k of paths) {
              ret[k] = obj[k];
            }
            return ret;
          }
          

          输入信息甚至允许自动完成:

          感谢DefinitelyTyped U extends keyof T 技巧!

          TypeScript Playground

          【讨论】:

          • @Nuthinking 你在用 VS Code 吗?
          • @Nuthinking 你把它放在一个 .ts 文件中?那应该行得通。我刚刚又试了一次
          • 来这里找这个,不错!
          • 实际上,我建议进行一项更改:function pick&lt;T extends object, U extends keyof T&gt;(obj: T, paths: Array&lt;U&gt;): Pick&lt;T, U&gt; Pick&lt;T,U&gt; 将正确键入返回的对象,推断为 any
          • 漂亮的解决方案!我有几个属性要选择,对象解构技巧在与 4 或 5 个属性一起使用时看起来非常糟糕。
          【解决方案10】:

          我添加了这个答案,因为没有一个答案使用Comma operator。

          使用destructuring assignment 和, 运算符非常简单

          const object = { a: 5, b: 6, c: 7  };
          const picked = ({a,c} = object, {a,c})
          
          console.log(picked);

          【讨论】:

          • 解构是最好的表达方式
          • 这个解决方案很聪明,但它在严格模式下不起作用(即'use strict')。我收到了ReferenceError: a is not defined。
          • 请注意,这种方法会使用两个变量 a 和 c 污染当前范围 - 注意不要根据上下文随机覆盖本地或全局变量。 (接受的答案通过在内联函数中使用两个局部变量来避免这个问题,这会在立即执行后超出范围。)
          • 命名空间污染使这完全不切实际。范围内已经有与对象属性匹配的变量是非常常见的,这就是存在 prop 速记 + 解构的原因。很可能您已经像原始示例中那样定义了高度或颜色。
          • 正确的做法是声明临时变量let a, c; const picked = ({a,c} = object, {a,c})。不幸的是,有一个很好的理由在其他答案中没有建议使用逗号运算符,它并不比const {a, c} = object; const picked = {a,c} 更容易。
          【解决方案11】:

          我想在这里提一下that 非常好的策展:

          pick-es2019.js

          Object.fromEntries(
            Object.entries(obj)
            .filter(([key]) => ['whitelisted', 'keys'].includes(key))
          );
          

          pick-es2017.js

          Object.entries(obj)
          .filter(([key]) => ['whitelisted', 'keys'].includes(key))
          .reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {});
          

          pick-es2015.js

          Object.keys(obj)
          .filter((key) => ['whitelisted', 'keys'].indexOf(key) >= 0)
          .reduce((newObj, key) => Object.assign(newObj, { [key]: obj[key] }), {})
          

          省略-es2019.js

          Object.fromEntries(
            Object.entries(obj)
            .filter(([key]) => !['blacklisted', 'keys'].includes(key))
          );
          

          省略-es2017.js

          Object.entries(obj)
          .filter(([key]) => !['blacklisted', 'keys'].includes(key))
          .reduce((obj, [key, val]) => Object.assign(obj, { [key]: val }), {});
          

          省略-es2015.js

          Object.keys(obj)
          .filter((key) => ['blacklisted', 'keys'].indexOf(key) < 0)
          .reduce((newObj, key) => Object.assign(newObj, { [key]: obj[key] }), {})
          

          【讨论】:

            【解决方案12】:

            我遇到了同样的问题,使用以下库轻松解决:

            object.pick

            https://www.npmjs.com/package/object.pick

            pick({a: 'a', b: 'b', c: 'c'}, ['a', 'b'])
            //=> {a: 'a', b: 'b'}
            

            object.省略

            https://www.npmjs.com/package/object.omit

            omit({a: 'a', b: 'b', c: 'c'}, ['a', 'c'])
            //=> { b: 'b' }
            

            【讨论】:

              【解决方案13】:

              动态解决方案

              ['color', 'height'].reduce((a,b) => (a[b]=elmo[b],a), {})
              

              let subset= (obj,keys)=> keys.reduce((a,b)=> (a[b]=obj[b],a),{});
              
              
              // TEST
              
              let elmo = { 
                color: 'red',
                annoying: true,
                height: 'unknown',
                meta: { one: '1', two: '2'}
              };
              
              console.log( subset(elmo, ['color', 'height']) );

              【讨论】:

                【解决方案14】:

                如果您使用的是 ES6,则有一种非常简洁的方法可以使用解构来做到这一点。解构使您可以使用扩展轻松添加到对象,但它也允许您以相同的方式制作子集对象。

                const object = {
                  a: 'a',
                  b: 'b',
                  c: 'c',
                  d: 'd',
                }
                
                // Remove "c" and "d" fields from original object:
                const {c, d, ...partialObject} = object;
                const subset = {c, d};
                
                console.log(partialObject) // => { a: 'a', b: 'b'}
                console.log(subset) // => { c: 'c', d: 'd'};
                

                【讨论】:

                • 这只适用于删除一个字段,而不是选择一个已知的子集?可能有无限数量的未知字段要删除,但这可能是某些人正在寻找的内容
                • 是的,但它可以删除几个已知字段,然后可以将这些字段重新分配给一个新对象,因此它仍然与这个问题相关。添加到答案中以进一步说明。
                • 这与@Ivan Nosov's answer 中的内容基本相同,尽管此处以更易于理解的方式进行了解释
                • @icc97 我认为这与伊万的回答相反。 Ivan 在 IIFE 中指定了一个子集,而这个答案是使用 spread 来基本上解构所有 except。即一个是黑名单,另一个是白名单。如果您使用 Lauren 的答案来解构包含敏感数据的对象(例如,用户会话),这可能很重要
                • 德拉特。不小心被否决了:-(。我再次单击向下按钮以撤消(正如提示经常指定的那样),但它反而锁定了我的投票!所以让我对此感到困惑。有时它会按预期工作,有时它会起到什么作用我想要!多年来一直无法预测。我不知道它是否取决于浏览器/操作系统/应用程序(现已弃用)/移动/触摸,还是什么!?偶尔我会进行无聊的编辑以更改投票。即使那样我也不知道它将如何反应。up 会恢复中立,还是成为 upvote?这里 upvote 很好。在其他地方我可能需要中立。但是如何?需要修复。
                【解决方案15】:

                将我的 2 美分加到 Ivan Nosov 答案:

                在我的情况下,我需要从对象中“切出”许多键,因此它变得非常丑陋并且不是一个非常动态的解决方案:

                const object = { a: 5, b: 6, c: 7, d: 8, aa: 5, bb: 6, cc: 7, dd: 8, aaa: 5, bbb: 6, ccc: 7, ddd: 8, ab: 5, bc: 6, cd: 7, de: 8  };
                const picked = (({ a, aa, aaa, ab, c, cc, ccc, cd }) => ({ a, aa, aaa, ab, c, cc, ccc, cd }))(object);
                
                console.log(picked);
                

                所以这里是一个使用 eval 的动态解决方案:

                const slice = (k, o) => eval(`(${k} => ${k})(o)`);
                
                
                const object    = { a: 5, b: 6, c: 7, d: 8, aa: 5, bb: 6, cc: 7, dd: 8, aaa: 5, bbb: 6, ccc: 7, ddd: 8, ab: 5, bc: 6, cd: 7, de: 8  };
                const sliceKeys = '({ a, aa, aaa, ab, c, cc, ccc, cd })';
                
                console.log( slice(sliceKeys, object) );
                

                【讨论】:

                  【解决方案16】:
                  1. 将参数转换为数组

                  2. 使用Array.forEach() 选择属性

                    Object.prototype.pick = function(...args) {
                       var obj = {};
                       args.forEach(k => obj[k] = this[k])
                       return obj
                    }
                    var a = {0:"a",1:"b",2:"c"}
                    var b = a.pick('1','2')  //output will be {1: "b", 2: "c"}
                    

                  【讨论】:

                  • 扩展原生类型的原型被认为是不好的做法,尽管它会起作用。 如果您正在编写库,请不要这样做。
                  【解决方案17】:

                  如question 中所述,在 JavaScript 中解构为动态命名变量是不可能的。

                  要动态设置键,您可以使用reduce函数而不改变对象,如下所示:

                  const getSubset = (obj, ...keys) => keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});
                  
                  const elmo = { 
                    color: 'red',
                    annoying: true,
                    height: 'unknown',
                    meta: { one: '1', two: '2'}
                  }
                  
                  const subset = getSubset(elmo, 'color', 'annoying')
                  console.log(subset)

                  应该注意,您在每次迭代时都会创建一个新对象,而不是更新单个克隆。 --mpen

                  下面是一个使用reduce和单克隆的版本(更新传入的初始值到reduce)。

                  const getSubset = (obj, ...keys) => keys.reduce((acc, curr) => {
                    acc[curr] = obj[curr]
                    return acc
                  }, {})
                  
                  const elmo = { 
                    color: 'red',
                    annoying: true,
                    height: 'unknown',
                    meta: { one: '1', two: '2'}
                  }
                  
                  const subset = getSubset(elmo, 'annoying', 'height', 'meta')
                  console.log(subset)

                  【讨论】:

                  • 太棒了!它让我一时没有意识到括号在 [c]: 上的重要性。我假设这以某种方式导致 c 被视为一个值而不是属性的名称。总之,很爽。 +1
                  • 谢谢伙计!这种用法是我喜欢 JavaScript 的一件事,它可以在不使用 eval 的情况下启用通用函数。简单地说,它的作用是让您在运行时将 dict 的键设置为变量。如果你定义了 var key = 'someKey',那么你可以将它用作 { [key]: 'value' },它给你 { someKey: 'value' }。真的很酷。
                  • 应该注意,您在每次迭代时都会创建一个新对象,而不是更新单个克隆。
                  • @mpen 很好的发现。我已经添加了一个版本变异单个克隆,正如您建议的那样,还传播 args 而不是传递一个键数组。
                  【解决方案18】:

                  试试

                  const elmo={color:"red",annoying:!0,height:"unknown",meta:{one:"1",two:"2"}};
                  
                  const {color, height} = elmo; newObject = ({color, height});
                  
                  console.log(newObject); //{ color: 'red', height: 'unknown' }
                  

                  【讨论】:

                    【解决方案19】:

                    使用对象解构和属性简写

                    const object = { a: 5, b: 6, c: 7  };
                    const picked = (({ a, c }) => ({ a, c }))(object);
                    
                    console.log(picked); // { a: 5, c: 7 }

                    来自 Philipp Kewisch:

                    这实际上只是一个被立即调用的匿名函数。所有这些都可以在 MDN 的 Destructuring Assignment 页面上找到。这是一个扩展的表格

                    let unwrap = ({a, c}) => ({a, c});
                    
                    let unwrap2 = function({a, c}) { return { a, c }; };
                    
                    let picked = unwrap({ a: 5, b: 6, c: 7 });
                    
                    let picked2 = unwrap2({a: 5, b: 6, c: 7})
                    
                    console.log(picked)
                    console.log(picked2)

                    【讨论】:

                    • 您是如何了解如何做到这一点的?在我见过的任何文档或文章(包括 MDN)中,都没有显示对象解构中使用的箭头语法。很高兴知道这一点。
                    • 这实际上只是一个被立即调用的匿名函数。所有这些都可以在 MDN 的 Destructuring Assignment 页面上找到。这是一个扩展的表格:let unwrap = ({a, c}) =&gt; ({a, c}); let unwrap2 = function({a, c}) { return { a, c }; }; let picked = unwrap({ a: 5, b: 6, c: 7 });
                    • 有没有办法用扩展运算符动态地做到这一点?
                    • @TomSarduy 如果要指定要删除的道具,可以使用 rest,例如const { b, ...picked } = object 将创建 picked 为 { a: 5, c: 7 }。您已指定简单地删除 b。不过,你的 eslint 可能会因为你声明一个你没有使用的 var 而生气。
                    • 这里的一个缺点是您需要完全键入两次属性名称系列。在需要选择许多属性的情况下,这可能是一个相当大的问题。
                    【解决方案20】:

                    虽然它有点冗长,但您可以使用 Array.prototype.reduce 完成 2 年前其他人推荐的下划线/lodash。

                    var subset = ['color', 'height'].reduce(function(o, k) { o[k] = elmo[k]; return o; }, {});
                    

                    这种方法从另一方面解决了这个问题:与其获取一个对象并将属性名称传递给它以进行提取,不如获取一个属性名称数组并将它们缩减为一个新对象。

                    虽然在最简单的情况下更冗长,但这里的回调非常方便,因为您可以轻松满足一些常见要求,例如将新对象的“颜色”属性更改为“颜色”、展平数组等——从一个服务/库接收对象并在其他地方构建所需的新对象时需要做的任何事情。虽然 underscore/lodash 是优秀且实现良好的库,但这是我的首选方法,可减少对供应商的依赖,并且当我的子集构建逻辑变得更复杂时,这是一种更简单、更一致的方法。

                    编辑:es7版本相同:

                    const subset = ['color', 'height'].reduce((a, e) => (a[e] = elmo[e], a), {});
                    

                    edit: 柯里化的一个很好的例子!让“pick”函数返回另一个函数。

                    const pick = (...props) => o => props.reduce((a, e) => ({ ...a, [e]: o[e] }), {});
                    

                    上面的方法与另一种方法非常接近,除了它可以让您动态构建一个“选择器”。例如

                    pick('color', 'height')(elmo);
                    

                    这种方法特别巧妙的是,您可以轻松地将选择的“选择”传递给任何需要函数的东西,例如Array#map:

                    [elmo, grover, bigBird].map(pick('color', 'height'));
                    // [
                    //   { color: 'red', height: 'short' },
                    //   { color: 'blue', height: 'medium' },
                    //   { color: 'yellow', height: 'tall' },
                    // ]
                    

                    【讨论】:

                    • es6 通过箭头函数和 Object.assign 的 return 使 this 变得更加简洁成为可能(因为分配给对象属性会返回属性值,但 Object.assign 会返回对象。)跨度>
                    • 另一个 es6 注意:你将很少需要这样做了,因为你通常只是解构赋值或参数。例如function showToy({ color, height }) { 只会把你需要的东西放在范围内。 reduce 方法主要适用于简化序列化对象时。
                    • 那个 ES6 版本的性能较差,因为它会在每次迭代时复制所有属性。它将 O(n) 操作变为 O(n^2)。您的第一个代码块的 ES6 等效项是 const pick = (obj, props) =&gt; props.reduce((a, e) =&gt; (a[e] = obj[e], a), {});
                    • @4castle 是的好电话 - 没有意义迭代这么多。我喜欢逗号语法 - 比一堆返回更好。
                    • @ShevchenkoViktor 我实际上在我原来的 es6 版本中使用了这种方法,但是在 @4castle 的评论之后改变了它。我认为传播更加清晰,但是对于代码中可能很容易成为瓶颈的较大对象(例如延迟从fetch返回的渲染数据)来说,这是一个巨大的差异,所以我建议添加注释来解释逗号运算符的使用.
                    【解决方案21】:

                    如果您已经在使用,请使用 lodash 库的 pick 方法。

                    var obj = { 'a': 1, 'b': '2', 'c': 3 };
                    
                    _.pick(object, ['a', 'c']);
                    
                    // => { 'a': 1, 'c': 3 }
                    

                    https://lodash.com/docs/4.17.10#pick

                    【讨论】:

                    • 我实际上并不想为该用例安装所有的 lodash 开销,但我尝试了上面所有的 reduce 方法,半小时后,我最终得到了一个干净可读的好旧 lodash一行代码。因为它是后端,所以并不重要。
                    • 相同。对于反应,有一个内联选择选项非常方便。例如,分配一个 props 子集以传递给组件。例如,可能需要在由 React Form 库包装的两个组件之间分配 initialValues。当然,我可以自己编写并将它(加上一个省略函数)放入实用程序文件中。到那时,也许我最好还是进口他们的……哈哈
                    【解决方案22】:

                    核心库没有内置类似的东西,但您可以使用对象解构来做到这一点......

                    const {color, height} = sourceObject;
                    const newObject = {color, height};
                    

                    你也可以写一个实用函数来做...

                    const cloneAndPluck = function(sourceObject, keys) {
                        const newObject = {};
                        keys.forEach((obj, key) => { newObject[key] = sourceObject[key]; });
                        return newObject;
                    };
                    
                    const subset = cloneAndPluck(elmo, ["color", "height"]);
                    

                    Lodash 等库也有 _.pick()。

                    【讨论】:

                    • 太好了,我只需要将 forEach 更改为:keys.forEach(key => { newObject[key] = sourceObject[key]; });。如果有意义,请更新评论。
                    【解决方案23】:

                    好老Array.prototype.reduce:

                    const selectable = {a: null, b: null};
                    const v = {a: true, b: 'yes', c: 4};
                    
                    const r = Object.keys(selectable).reduce((a, b) => {
                      return (a[b] = v[b]), a;
                    }, {});
                    
                    console.log(r);
                    

                    这个答案也使用了神奇的逗号运算符: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator

                    如果你想变得很花哨,这个更紧凑:

                    const r = Object.keys(selectable).reduce((a, b) => (a[b] = v[b], a), {});
                    

                    将它们组合成一个可重用的函数:

                    const getSelectable = function (selectable, original) {
                      return Object.keys(selectable).reduce((a, b) => (a[b] = original[b], a), {})
                    };
                    
                    const r = getSelectable(selectable, v);
                    console.log(r);
                    

                    【讨论】:

                      【解决方案24】:

                      另一种解决方案:

                      var subset = {
                         color: elmo.color,
                         height: elmo.height 
                      }
                      

                      这对我来说看起来比迄今为止几乎任何答案都更具可读性,但也许这只是我!

                      【讨论】:

                      • 我更喜欢高效而不是花哨但令人困惑的代码,在现实生活中的软件工程中,这是最具可读性和可维护性的解决方案。
                      • 是的,但是,对我来说,使用解构和速记符号的一个好处是它不太容易出错。如果我每次错误地复制和粘贴代码以得到subset = {color: elmo.color, height: elmo.color} 时都能得到一分钱,那我至少会有一分钱……好吧,也许是一角钱。
                      • 我不会称解构速记不太容易出错,因为它不是 D.R.Y.
                      • 我必须同意。在不使用不需要的变量污染上下文的情况下,这是迄今为止最易读的解决方案。其余的看起来太混乱了。我更喜欢在看到我的代码时就理解它。
                      • 我倾向于同意这一点......解构+匿名函数同样冗长。如果您经常这样做,那么更具声明性的“实用”函数之一将值得使用
                      【解决方案25】:

                      只是另一种方式......

                      var elmo = { 
                        color: 'red',
                        annoying: true,
                        height: 'unknown',
                        meta: { one: '1', two: '2'}
                      }
                      
                      var subset = [elmo].map(x => ({
                        color: x.color,
                        height: x.height
                      }))[0]
                      

                      您可以将此函数与对象数组一起使用 =)

                      【讨论】:

                        【解决方案26】:

                        这在 Chrome 控制台中适用于我。这有什么问题吗?

                        var { color, height } = elmo
                        var subelmo = { color, height }
                        console.log(subelmo) // {color: "red", height: "unknown"}
                        

                        【讨论】:

                        • 这读起来不错,但会创建两个不必要的变量,颜色和高度。
                        • 不理解您的评论。 OP 的要求是创建一个具有这两个元素的对象
                        • @MSi 这不仅创建了一个对象,还创建了两个变量。
                        【解决方案27】:

                        使用动态属性解构赋值

                        此解决方案不仅适用于您的具体示例,而且更普遍适用:

                        const subset2 = (x, y) => ({[x]:a, [y]:b}) => ({[x]:a, [y]:b});
                        
                        const subset3 = (x, y, z) => ({[x]:a, [y]:b, [z]:c}) => ({[x]:a, [y]:b, [z]:c});
                        
                        // const subset4...etc.
                        
                        
                        const o = {a:1, b:2, c:3, d:4, e:5};
                        
                        
                        const pickBD = subset2("b", "d");
                        const pickACE = subset3("a", "c", "e");
                        
                        
                        console.log(
                          pickBD(o), // {b:2, d:4}
                          pickACE(o) // {a:1, c:3, e:5}
                        );

                        您可以轻松定义subset4 等以考虑更多属性。

                        【讨论】:

                          【解决方案28】:

                          注意:虽然最初的问题是针对 javascript,但它可以是 通过以下解决方案完成 jQuery

                          如果你愿意,你可以扩展 jquery 这里是一个切片的示例代码:

                          jQuery.extend({
                            sliceMe: function(obj, str) {
                                var returnJsonObj = null;
                              $.each( obj, function(name, value){
                                  alert("name: "+name+", value: "+value);
                                  if(name==str){
                                      returnJsonObj = JSON.stringify("{"+name+":"+value+"}");
                                  }
                          
                              });
                                return returnJsonObj;
                            }
                          });
                          
                          var elmo = { 
                            color: 'red',
                            annoying: true,
                            height: 'unknown',
                            meta: { one: '1', two: '2'}
                          };
                          
                          
                          var temp = $.sliceMe(elmo,"color");
                          alert(JSON.stringify(temp));
                          

                          这是相同的小提琴:http://jsfiddle.net/w633z/

                          【讨论】:

                          • @ChristianSchlensker 这是 javascript
                          【解决方案29】:

                          我建议看看Lodash;它有很多很棒的实用功能。

                          例如,pick() 正是您所寻求的:

                          var subset = _.pick(elmo, ['color', 'height']);
                          

                          fiddle

                          【讨论】:

                          • underscore.js 相同
                          • 是否有任何功能可以仅排除某些字段而不是选择?所以我的 json 中有大约 50 个字段,并且想要除了 2 个字段之外的所有内容。
                          • 是的!您可以使用_.omit(elmo, ['voice']) 返回除voice 之外的所有内容
                          • 我不喜欢这种方法的一点是,您将字段名称放在引号中,因此很容易出现拼写错误,常见的重构(例如重命名 IDE 中的属性)无法识别,等等等
                          • 您不需要下划线/lodash 来完成此操作。我相信 vanilla js 解决方案更好。
                          【解决方案30】:

                          您也可以使用Lodash。

                          var subset = _.pick(elmo ,'color', 'height');
                          

                          作为补充,假设您有一个“elmo”数组:

                          elmos = [{ 
                                color: 'red',
                                annoying: true,
                                height: 'unknown',
                                meta: { one: '1', two: '2'}
                              },{ 
                                color: 'blue',
                                annoying: true,
                                height: 'known',
                                meta: { one: '1', two: '2'}
                              },{ 
                                color: 'yellow',
                                annoying: false,
                                height: 'unknown',
                                meta: { one: '1', two: '2'}
                              }
                          ];
                          

                          如果您想要相同的行为,使用 lodash,您只需:

                          var subsets = _.map(elmos, function(elm) { return _.pick(elm, 'color', 'height'); });
                          

                          【讨论】:

                            猜你喜欢
                            • 2018-11-30
                            • 1970-01-01
                            • 2021-01-18
                            相关资源
                            最近更新 更多