从对象数组中提取属性的值作为数组
我有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是一样的,因为返回的数组的长度和提供的数组是一样的。 (在这种情况下, map
比reduce
便宜):
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]});