【问题标题】:From an array of objects, extract value of a property as array从对象数组中,将属性的值提取为数组
【发布时间】:2022-01-10 18:41:36
【问题描述】:

我有具有以下结构的 JavaScript 对象数组:

objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];

我想从每个对象中提取一个字段,并获取一个包含值的数组,例如字段foo 将给出数组[ 1, 3, 5 ]

我可以用这个简单的方法做到这一点:

function getFields(input, field) {
    var output = [];
    for (var i=0; i < input.length ; ++i)
        output.push(input[i][field]);
    return output;
}

var result = getFields(objArray, "foo"); // returns [ 1, 3, 5 ]

有没有更优雅或惯用的方法来做到这一点,这样就不需要自定义实用函数了?


注意suggested duplicate,它介绍了如何将单个对象转换为数组。

【问题讨论】:

  • Prototype 库为 Array 原型添加了一个“pluck”函数(我认为),所以你可以写 var foos = objArray.pluck("foo");
  • @hyde - jsperf.com/map-vs-native-for-loop - 请看看这个,希望普通循环本身是一个好的解决方案
  • @N20084753 为了公平测试,您还应该比较存在的原生 Array.prototype.map 函数
  • OP,比起其他建议,我更喜欢你的方法。没有错。

标签: javascript javascript-objects


【解决方案1】:

这是实现它的更短的方法:

let result = objArray.map(a => a.foo);

let result = objArray.map(({ foo }) => foo)

您也可以查看Array.prototype.map()

【讨论】:

  • 嗯,这与 totymedli 的另一个答案的评论相同,但无论如何(在我看来)它实际上比其他 答案 更好,所以.. . 将其更改为接受的答案。
  • 我真的很喜欢那个,顺便说一句,=&gt; 符号对我来说闻起来像是新事物,所以我认为如果该解决方案实际上可行,则需要对其兼容性进行审查。
  • @PauloRoberto 箭头函数是basically supported everywhere except IE.
  • 当然,这是允许的,但是恕我直言,没有什么可以客观地使这个答案变得更好,除了它使用的语法在您提出问题时 不可用 并且某些浏览器甚至不支持。我还要注意,此答案是 cmets 的直接副本,该副本是在发布此答案近一年前根据最初接受的答案制作的。
  • @Alnitak 使用更新的功能,在我看来,客观上更好。这个 sn-p 非常常见,所以我不相信这是抄袭。将过时的答案固定在顶部确实没有任何价值。
【解决方案2】:

是的,但它依赖于 JavaScript 的 ES5 特性。这意味着它无法在 IE8 或更早版本中运行。

var result = objArray.map(function(a) {return a.foo;});

在 ES6 兼容的 JS 解释器上,您可以使用 arrow function 为简洁起见:

var result = objArray.map(a => a.foo);

Array.prototype.map documentation

【讨论】:

    【解决方案3】:

    对于仅限 JS 的解决方案,我发现,尽管可能不优雅,但简单的索引 for 循环比其替代方案性能更高。

    从 100000 个元素的数组中提取单个属性(通过 jsPerf)

    传统的 for 循环 368 Ops/sec

    var vals=[];
    for(var i=0;i<testArray.length;i++){
       vals.push(testArray[i].val);
    }
    

    ES6 for..of 循环 303 Ops/sec

    var vals=[];
    for(var item of testArray){
       vals.push(item.val); 
    }
    

    Array.prototype.map 19 Ops/sec

    var vals = testArray.map(function(a) {return a.val;});
    

    TL;DR - .map() 很慢,但如果您觉得可读性比性能更重要,请随意使用它。

    编辑 #2:6/2019 - jsPerf 链接断开,已删除。

    【讨论】:

    • 我刚刚用jsBench重新测试了它(非常感谢Mirko Vukušić创建了jsPerf的替代品),结果仍然the same - for是最快的,for .. of稍微慢一点,map 慢一半左右。
    【解决方案4】:

    查看Lodash's _.pluck() 函数或Underscore's _.pluck() 函数。两者都在一个函数调用中完全满足您的需求!

    var result = _.pluck(objArray, 'foo');
    

    更新: _.pluck() has been removed as of Lodash v4.0.0,支持 _.map() 与类似于 Niet's answer 的组合。 _.pluck() is still available in Underscore.

    更新 2: 正如 Mark 指出的 in the comments,介于 Lodash v4 和 4.3 之间,添加了一个新功能,再次提供此功能。 _.property() 是一个速记函数,它返回一个用于获取对象中属性值的函数。

    此外,_.map() 现在允许将字符串作为第二个参数传入,该参数将传入_.property()。因此,以下两行代码等同于上面 Lodash 4 之前的代码示例。

    var result = _.map(objArray, 'foo');
    var result = _.map(objArray, _.property('foo'));
    

    _.property()_.map() 也允许您提供以点分隔的字符串或数组以访问子属性:

    var objArray = [
        {
            someProperty: { aNumber: 5 }
        },
        {
            someProperty: { aNumber: 2 }
        },
        {
            someProperty: { aNumber: 9 }
        }
    ];
    var result = _.map(objArray, _.property('someProperty.aNumber'));
    var result = _.map(objArray, _.property(['someProperty', 'aNumber']));
    

    以上示例中的两个_.map() 调用都将返回[5, 2, 9]

    如果您更喜欢函数式编程,请查看Ramda's R.pluck() 函数,它看起来像这样:

    var result = R.pluck('foo')(objArray);  // or just R.pluck('foo', objArray)
    

    【讨论】:

      【解决方案5】:

      最好使用某种库,如 lodash 或 underscore 来保证跨浏览器。

      在 Lodash 中,您可以通过以下方法获取数组中属性的值

      _.map(objArray,"foo")
      

      并在下划线中

      _.pluck(objArray,"foo")
      

      两者都会返回

      [1, 2, 3]
      

      【讨论】:

        【解决方案6】:

        使用Array.prototype.map

        function getFields(input, field) {
            return input.map(function(o) {
                return o[field];
            });
        }
        

        请参阅上面的链接以获取适用于 ES5 之前的浏览器的 shim。

        【讨论】:

          【解决方案7】:

          在 ES6 中,你可以这样做:

          const objArray = [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}]
          objArray.map(({ foo }) => foo)
          

          【讨论】:

            【解决方案8】:

            从对象数组中收集不同字段的示例

            let inputArray = [
              { id: 1, name: "name1", value: "value1" },
              { id: 2, name: "name2", value: "value2" },
            ];
            
            let ids = inputArray.map( (item) => item.id);
            let names = inputArray.map((item) => item.name);
            let values = inputArray.map((item) => item.value);
            
            console.log(ids);
            console.log(names);
            console.log(values);

            结果:

            [ 1, 2 ]
            [ 'name1', 'name2' ]
            [ 'value1', 'value2' ]
            

            【讨论】:

              【解决方案9】:

              map() 方法创建一个新数组,其中填充了对调用数组中的每个元素调用提供的函数的结果。

              let kvArray = [{key: 1, value: 10},
                             {key: 2, value: 20},
                             {key: 3, value: 30}]
              
              let reformattedArray = kvArray.map(obj => {
                 return obj.value
              })
              

              或者

              const kvArray = [['key1', 'value1'], ['key2', 'value2']]
              
              // Use the regular Map constructor to transform a 2D key-value Array into a map
              const myMap = new Map(kvArray)
              
              myMap.get('key1') // returns "value1"
              
              // Use Array.from() to transform a map into a 2D key-value Array
              console.log(Array.from(myMap)) // Will show you exactly the same Array as kvArray
              
              // A succinct way to do the same, using the spread syntax
              console.log([...myMap])
              
              // Or use the keys() or values() iterators, and convert them to an array
              console.log(Array.from(myMap.keys())) // ["key1", "key2"]
              

              【讨论】:

                【解决方案10】:

                虽然map 是从对象列表中选择“列”的合适解决方案,但它有一个缺点。如果没有明确检查列是否存在,它会抛出错误并(最多)为您提供undefined。 我会选择reduce 解决方案,它可以简单地忽略该属性,甚至为您设置一个默认值。

                function getFields(list, field) {
                    //  reduce the provided list to an array only containing the requested field
                    return list.reduce(function(carry, item) {
                        //  check if the item is actually an object and does contain the field
                        if (typeof item === 'object' && field in item) {
                            carry.push(item[field]);
                        }
                
                        //  return the 'carry' (which is the list of matched field values)
                        return carry;
                    }, []);
                }
                

                jsbin example

                即使提供的列表中的一项不是对象或不包含该字段,这也可以工作。

                如果项目不是对象或不包含字段,它甚至可以通过协商默认值来变得更加灵活。

                function getFields(list, field, otherwise) {
                    //  reduce the provided list to an array containing either the requested field or the alternative value
                    return list.reduce(function(carry, item) {
                        //  If item is an object and contains the field, add its value and the value of otherwise if not
                        carry.push(typeof item === 'object' && field in item ? item[field] : otherwise);
                
                        //  return the 'carry' (which is the list of matched field values)
                        return carry;
                    }, []);
                }
                

                jsbin example

                这与 map 相同,因为返回数组的长度与提供的数组相同。 (在这种情况下mapreduce 稍微便宜一些):

                function getFields(list, field, otherwise) {
                    //  map the provided list to an array containing either the requested field or the alternative value
                    return list.map(function(item) {
                        //  If item is an object and contains the field, add its value and the value of otherwise if not
                        return typeof item === 'object' && field in item ? item[field] : otherwise;
                    }, []);
                }
                

                jsbin example

                然后是最灵活的解决方案,它让您只需提供一个替代值即可在两种行为之间切换。

                function getFields(list, field, otherwise) {
                    //  determine once whether or not to use the 'otherwise'
                    var alt = typeof otherwise !== 'undefined';
                
                    //  reduce the provided list to an array only containing the requested field
                    return list.reduce(function(carry, item) {
                        //  If item is an object and contains the field, add its value and the value of 'otherwise' if it was provided
                        if (typeof item === 'object' && field in item) {
                            carry.push(item[field]);
                        }
                        else if (alt) {
                            carry.push(otherwise);
                        }
                
                        //  return the 'carry' (which is the list of matched field values)
                        return carry;
                    }, []);
                }
                

                jsbin example

                正如上面的示例(希望)阐明了它的工作方式,让我们通过使用 Array.concat 函数来稍微缩短函数。

                function getFields(list, field, otherwise) {
                    var alt = typeof otherwise !== 'undefined';
                
                    return list.reduce(function(carry, item) {
                        return carry.concat(typeof item === 'object' && field in item ? item[field] : (alt ? otherwise : []));
                    }, []);
                }
                

                jsbin example

                【讨论】:

                  【解决方案11】:

                  一般来说,如果您想推断数组内的对象值(如问题中所述),那么您可以使用 reduce、map 和数组解构。

                  ES6

                  let a = [{ z: 'word', c: 'again', d: 'some' }, { u: '1', r: '2', i: '3' }];
                  let b = a.reduce((acc, obj) => [...acc, Object.values(obj).map(y => y)], []);
                  
                  console.log(b)
                  

                  使用 for in 循环的等价物是:

                  for (let i in a) {
                    let temp = [];
                    for (let j in a[i]) {
                      temp.push(a[i][j]);
                    }
                    array.push(temp);
                  }
                  

                  产生的输出: ["word", "again", "some", "1", "2", "3"]

                  【讨论】:

                    【解决方案12】:

                    如果您还想支持类似数组的对象,请使用Array.from (ES2015):

                    Array.from(arrayLike, x => x.foo);
                    

                    它比 Array.prototype.map() 方法的优点是输入也可以是 Set

                    let arrayLike = new Set([{foo: 1}, {foo: 2}, {foo: 3}]);
                    

                    【讨论】:

                      【解决方案13】:

                      如果你想在 ES6+ 中使用多个值,下面的方法会起作用

                      objArray = [ { foo: 1, bar: 2, baz: 9}, { foo: 3, bar: 4, baz: 10}, { foo: 5, bar: 6, baz: 20} ];
                      
                      let result = objArray.map(({ foo, baz }) => ({ foo, baz }))
                      

                      这是因为左侧的{foo, baz} 使用object destructoring,而箭头右侧由于ES6's enhanced object literals 而等效于{foo: foo, baz: baz}

                      【讨论】:

                        【解决方案14】:

                        如果你有嵌套数组,你可以这样工作:

                        const objArray = [ 
                             { id: 1, items: { foo:4, bar: 2}},
                             { id: 2, items: { foo:3, bar: 2}},
                             { id: 3, items: { foo:1, bar: 2}} 
                            ];
                        
                            let result = objArray.map(({id, items: {foo}}) => ({id, foo}))
                            
                            console.log(result)

                        【讨论】:

                          【解决方案15】:

                          这取决于你对“更好”的定义。

                          其他答案指出了 map 的使用,这很自然(特别是对于习惯于功能风格的人)并且简洁。我强烈推荐使用它(如果你不为少数 IE8-IT 人所困扰)。因此,如果“更好”意味着“更简洁”、“可维护”、“可理解”,那么是的,它会更好。

                          另一方面,这种美丽并非没有额外的成本。我不是 microbench 的忠实粉丝,但我提出了一个小的 test here。结果可想而知,旧的丑陋方式似乎比地图功能更快。因此,如果“更好”意味着“更快”,那么不,请保持老派时尚。

                          这只是一个微型工作台,绝不反对使用map,这只是我的两分钱:)。

                          【讨论】:

                            【解决方案16】:

                            从对象数组中轻松提取多个属性:

                            let arrayOfObjects = [
                              {id:1, name:'one', desc:'something'},
                              {id:2, name:'two', desc:'something else'}
                            ];
                            
                            //below will extract just the id and name
                            let result = arrayOfObjects.map(({id, name}) => ({id, name}));
                            

                            result 将是 [{id:1, name:'one'},{id:2, name:'two'}]

                            根据需要在地图函数中添加或删除属性

                            【讨论】:

                              【解决方案17】:

                              上述答案适用于单个属性,但当从数组中选择多个属性时使用此

                              var arrayObj=[{Name,'A',Age:20,Email:'a.gmail.com'},{Name,'B',Age:30,Email:'b.gmail.com'},{Name,'C',Age:40,Email:'c.gmail.com'}]
                              

                              现在我只选择两个字段

                               var outPutArray=arrayObj.map(( {Name,Email} ) =>  ({Name,Email}) )
                               console.log(outPutArray)
                              

                              【讨论】:

                                【解决方案18】:

                                以上提供的答案适用于提取单个属性,如果您想从对象数组中提取多个属性,该怎么办。 这是解决方案! 在这种情况下,我们可以简单地使用 _.pick(object, [paths])

                                _.pick(object, [paths])
                                

                                假设 objArray 的对象具有如下三个属性

                                objArray = [ { foo: 1, bar: 2, car:10}, { foo: 3, bar: 4, car:10}, { foo: 5, bar: 6, car:10} ];
                                

                                现在我们要从每个对象中提取 foo 和 bar 属性并将它们存储在一个单独的数组中。 首先,我们将使用 map 迭代数组元素,然后在其上应用 Lodash 库标准 _.pick() 方法。

                                现在我们可以提取 'foo' 和 'bar' 属性了。

                                var newArray = objArray.map((element)=>{ return _.pick(element, ['foo','bar'])}) console.log(newArray);

                                结果是 [{foo: 1, bar: 2},{foo: 3, bar: 4},{foo: 5, bar: 6}]

                                享受!!!

                                【讨论】:

                                • _.pick 来自哪里?这不是标准功能。
                                • 我刚刚更新了答案,_.pick() 是 Lodash 库的标准方法。
                                【解决方案19】:

                                在 ES6 中,如果您想将字段作为字符串动态传递:

                                function getFields(array, field) {
                                    return array.map(a => a[field]);
                                }
                                
                                let result = getFields(array, 'foo');
                                

                                【讨论】:

                                  【解决方案20】:

                                  创建一个空数组,然后从您的列表中为每个元素创建一个空数组,然后将该对象中您想要的内容推送到您的空数组中。

                                   let objArray2 = [];
                                   objArray.forEach(arr => objArray2.push(arr.foo));
                                  

                                  【讨论】:

                                  • 评论已编辑,感谢您的解释。
                                  【解决方案21】:

                                  在处理对象数组时,函数映射是一个不错的选择。尽管已经发布了许多很好的答案,但将 map 与 filter 结合使用的示例可能会有所帮助。

                                  如果您想排除未定义值的属性或仅排除特定属性,您可以执行以下操作:

                                      var obj = {value1: "val1", value2: "val2", Ndb_No: "testing", myVal: undefined};
                                      var keysFiltered = Object.keys(obj).filter(function(item){return !(item == "Ndb_No" || obj[item] == undefined)});
                                      var valuesFiltered = keysFiltered.map(function(item) {return obj[item]});
                                  

                                  https://jsfiddle.net/ohea7mgk/

                                  【讨论】:

                                    【解决方案22】:

                                    这是在对象数组上使用map 方法来获取特定属性的另一种形式:

                                    const objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
                                    
                                    const getProp = prop => obj => obj[prop];
                                    const getFoo = getProp('foo');
                                    const fooes = objArray.map(getFoo);
                                    console.log(fooes);

                                    【讨论】:

                                      猜你喜欢
                                      相关资源
                                      最近更新 更多