From an array of objects, extract value of a property as array
我有以下结构的javascript对象数组:
1 | objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ]; |
我想从每个对象中提取一个字段,并得到一个包含这些值的数组,例如,字段
我可以用这种简单的方法做到这一点:
1 2 3 4 5 6 7 8 | 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 ] |
有没有更优雅或更惯用的方法来实现这一点,这样就不需要自定义实用程序功能了?
注意关于建议的复制,它涉及如何将单个对象转换为数组。
以下是实现这一目标的较短方法:
1 | let result = objArray.map(a => a.foo); |
或
1 | let result = objArray.map(({ foo }) => foo) |
您还可以查看MDN上的
是的,但它依赖于JavaScript的ES5特性。这意味着它在IE8或更高版本中不起作用。
1 | var result = objArray.map(function(a) {return a.foo;}); |
在ES6兼容的JS解释器上,您可以使用箭头函数来简化:
1 | var result = objArray.map(a => a.foo); |
文档
查看lodash的
1 | var result = _.pluck(objArray, 'foo'); |
更新:从Lodash v4.0.0开始,
更新2:正如Mark在评论中指出的,在Lodash v4和4.3之间的某个地方,添加了一个新的函数,它再次提供了这个功能。
此外,
1 2 | var result = _.map(objArray, 'foo'); var result = _.map(objArray, _.property('foo')); |
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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'])); |
上述示例中的两个
如果您对函数式编程有更多的了解,请看一下Ramda的
1 | var result = R.pluck('foo')(objArray); // or just R.pluck('foo', objArray) |
说到纯JS的解决方案,我发现一个简单的索引
https://jspef.com/extract-prop-from-object-array/
从100000元素数组中提取单个属性
传统for循环368次/秒
1 2 3 4 | var vals=[]; for(var i=0;i<testArray.length;i++){ vals.push(testArray[i].val); } |
ES6用于303次循环的..操作/秒
1 2 3 4 | var vals=[]; for(var item of testArray){ vals.push(item.val); } |
array.prototype.map 19次/秒
1 | var vals = testArray.map(function(a) {return a.val;}); |
编辑:Ops/s更新于2017年10月。tl;dr-.map()速度慢。但有时可读性比性能更重要。
使用
1 2 3 4 5 | function getFields(input, field) { return input.map(function(o) { return o[field]; }); } |
有关pre-ES5浏览器的填充程序,请参见上面的链接。
最好使用像lodash或下划线这样的库来保证跨浏览器的安全。
在Lodash中,可以通过以下方法获取数组中属性的值
1 | _.map(objArray,"foo") |
在下划线中
1 | _.pluck(objArray,"foo") |
两者都将返回[1,3,5]
在ES6中,您可以执行以下操作:
1 2 | const objArray = [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}] objArray.map(({ foo }) => foo) |
虽然
1 2 3 4 5 6 7 8 9 10 11 12 | 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; }, []); } |
JSB实例
即使所提供列表中的某个项不是对象或不包含字段,也可以执行此操作。
如果项目不是对象或不包含字段,则可以通过协商默认值使其更灵活。
1 2 3 4 5 6 7 8 9 10 | 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; }, []); } |
JSB实例
这与map相同,因为返回的数组的长度与提供的数组的长度相同。(在这种情况下,
1 2 3 4 5 6 7 | 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; }, []); } |
JSB实例
还有一个最灵活的解决方案,它可以让你通过提供一个替代值在两种行为之间切换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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; }, []); } |
JSB实例
正如上面的例子(希望)揭示了这种工作方式,让我们通过使用
1 2 3 4 5 6 7 | 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 : [])); }, []); } |
JSB实例
这取决于你对"更好"的定义。
其他答案指出了地图的使用,这是自然的(特别是对于习惯了功能风格的人)和简洁的。我强烈建议你使用它(如果你不为几个IE8-IT的家伙费心的话)。所以,如果"更好"的意思是"更简洁"、"可维护"、"可理解",那么是的,这就更好了。
另一方面,这种美不可能没有额外的成本。我不喜欢微生物,但我在这里做了一个小测试。结果是可以预测的,旧的丑陋的方法似乎比映射函数更快。所以,如果"更好"的意思是"更快",那么就不,保持传统的时尚。
再说一遍,这只是一个微生物,决不是反对使用
一般来说,如果您想推断数组中的对象值(如问题中所述),那么可以使用reduce、map和array析构化。
ES6
1 2 3 | let a = [{ z: 'word', c: 'again', d: 'some' }, { u: '1', r: '2', i: '3' }]; let b = a.reduce((acc, x) => [...acc, Object.values(x).map((y, i) => y)], []); console.log(b) |
在循环中等效使用如下:
1 2 3 4 5 6 7 | 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"]
如果还希望支持类似数组的对象,请使用array.from(es2015):
1 | Array.from(arrayLike, x => x.foo); |
与array.prototype.map()方法相比,它的优势在于输入也可以是一个集合:
1 | let arrayLike = new Set([{foo: 1}, {foo: 2}, {foo: 3}]); |
函数映射是处理对象数组时的一个好选择。尽管已经发布了许多好的答案,但是使用map和filter组合的示例可能会有所帮助。
如果要排除未定义值的属性或只排除特定属性,可以执行以下操作:
1 2 3 | 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/ohea7mgkk/
如果您希望在ES6+中有多个值,则以下内容将有效
1 2 3 | 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 })) |
这是因为左边的
上面提供的答案对于提取单个属性很好,如果您想从对象数组中提取多个属性,该怎么办?这是解决方案!!在这种情况下,我们可以简单地使用pick(对象,[路径])
1 | _.pick(object, [paths]) |
假设objarray具有以下三个属性的对象
1 | 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"属性。
console.log(newArray);
结果是[foo:1,bar:2,foo:3,bar:4,foo:5,bar:6]
享受!!!!