从对象数组中提取属性的值作为数组

我有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 ] 

有没有“更好”(更短,更高效和/或更习惯)的方式?


注意关于build议的重复 ,该问题询问如何将对象转换数组,这个问题询问如何对象数组中提取单个属性。

这是实现它的一个较短的方法

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

是的,但它依赖于JavaScript的更新function。 这意味着它将无法在IE8或更高版本中使用。

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

文档

检查Lodash的_.pluck()函数或_.pluck()_.pluck()函数。 在一个函数调用中,它们都完全符合你的要求!

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

更新: _.pluck()已经从Lodash v4.0.0中被移除 ,赞成_.map()和类似于Niet的答案 。 _.pluck()_.pluck()中仍然可用 。

更新2:正如Mark 在评论中指出的 ,在Lodash v4和4.3之间的某个地方,增加了一个新的function,可以再次提供这个function。 _.property()是一个简写函数,它返回一个获取对象属性值的函数。

另外, _.map()现在允许一个string作为第二个参数传入_.property() 。 因此,以下两行代码与前面Lodash 4中的代码示例相同。

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

_.property() ,因此_.map()也允许你提供一个点分隔的string或数组,以便访问子属性:

 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] _.map() [5, 2, 9]

如果你对函数式编程有点多,可以看看R.pluck()R.pluck()函数,它看起来像这样:

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

说到JS的解决scheme,我发现,尽pipe它可能是不雅观的,但是一个简单的索引循环比它的替代品更高效。

https://jsperf.com/extract-prop-from-object-array/

从100000元素数组中提取单个属性

传统的循环 368 Ops /秒

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

ES6 for循环 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;}); 

编辑:Ops / s更新10/2017。 TL; DR – .map()很慢。 但有时可读性比性能更值得。

使用Array.prototype.map

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

请参阅上面的链接,了解ES5之前的浏览器。

最好使用一些类似lodash或下划线的库来保证跨浏览器。

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

 _.map(objArray,"foo") 

并在下划线

 _.pluck(objArray,"foo") 

两者都会返回[1,3,5]

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

 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的例子

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

如果一个项目不是一个对象或者不包含这个字段,它甚至可以通过谈判一个默认值更加灵活。

 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的例子

这和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的例子

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

 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的例子

正如上面的例子(希望)揭示了这个工作的方式,让我们利用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的例子

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

其他的答案指出了地图的使用,这是很自然的(特别是对于习惯风格的人来说)并且简洁。 我强烈build议使用它(如果你不打扰less数IE8的IT人员)。 所以如果“更好”的意思是“更简洁”,“可维护”,“可以理解”,那么是的,这样会更好。

另一方面,这个美丽不是没有额外的成本。 我不是微型平台的忠实粉丝,但我在这里做了一个小testing 。 结果是可预见的,旧的丑陋的方式似乎比地图function更快。 所以如果“更好”的意思是“更快”,那么不行,留守老派的时尚。

再次,这只是一个微型平台,决不支持使用map ,这只是我的两分钱:)。

处理对象数组时,函数图是一个不错的select。 虽然已经有了很多好的答案,但是使用地图和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/