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

我有具有以下结构的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 ]

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


注意建议重复,它介绍了如何将单个对象转换为数组。

1515604 次浏览

是的,但它依赖于JavaScript的ES5功能。这意味着它在IE8或更早版本中不起作用。

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

在ES6兼容的JS解释器上,为了简洁起见,您可以使用箭头函数

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

Array.prototype.map留档

使用#0

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

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

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

其他答案指出了map的使用,这是自然的(特别是对于习惯于函数式风格的人)和简洁的。我强烈推荐使用它(如果你不介意少数IE8-IT人员)。所以如果“更好”意味着“更简洁”、“可维护”、“可理解”,那么是的,它要好得多。

另一方面,这种美感并不是没有额外成本的。我不是微板凳的忠实粉丝,但我放了一个小在这里测试。结果是可以预测的,旧的丑陋方式似乎比地图功能更快。所以如果“更好”意味着“更快”,那么不,保持老派时尚。

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

查看Lodash的#0函数或下划线#0函数。两者都在单个函数调用中完全按照您的要求执行!

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

更新时间:#0已从Lodash v4.0.0中删除,赞成_.map()与类似于Niet的回答的东西结合。

更新2:正如Mark指出的在评论,在Lodash v4和4.3之间的某个地方,添加了一个新函数再次提供此功能。#0是一个速记函数,它返回一个用于获取对象中属性值的函数。

此外,_.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]

如果你更喜欢函数式编程,看看拉姆达的#0函数,它看起来像这样:

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

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

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

    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]});

最好使用某种类型的库,如洛达什或下划线,以实现跨浏览器保证。

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

_.map(objArray,"foo")

和下划线

_.pluck(objArray,"foo")

两人都会回来

[1, 2, 3]

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

function getFields(list, field) {//  reduce the provided list to an array only containing the requested fieldreturn list.reduce(function(carry, item) {//  check if the item is actually an object and does contain the fieldif (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 valuereturn list.reduce(function(carry, item) {//  If item is an object and contains the field, add its value and the value of otherwise if notcarry.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 valuereturn list.map(function(item) {//  If item is an object and contains the field, add its value and the value of otherwise if notreturn typeof item === 'object' && field in item ? item[field] : otherwise;}, []);}

jsbin的例子

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

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 fieldreturn 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 providedif (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的例子

对于仅限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操作/秒

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;});

太长别读-. map()很慢,但如果您觉得易读性比性能更有价值,请随意使用它。

编辑#2:6/2019-jsPerf链接损坏,删除。

以下是实现它的较短方法:

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

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

您也可以查看#0

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

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

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

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

在ES6中,您可以:

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

一般来说,如果你想外推数组中的对象值(如问题中所述),那么你可以使用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 (let i in a) {let temp = [];for (let j in a[i]) {temp.push(a[i][j]);}array.push(temp);}

产出:["word","再次","一些","1","2","3"]

上面提供的答案适合提取单个属性,如果你想从对象数组中提取多个属性怎么办。这就是解决方案!!在这种情况下,我们可以简单地使用_。

_.pick(object, [paths])

让我们假设ObjectArray具有如下三个属性的对象

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

现在我们想从每个对象中提取foo和bar属性并将它们存储在一个单独的数组中。首先,我们将使用map迭代数组元素,然后我们对其应用Lodash Library Standard_。

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

var newArray=objArray.map((元素)=>{返回_选择(元素,['foo','bar'])})console.log(newArray);

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

享受!!!

如果您想要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}使用对象解构,而右边的箭头相当于{foo: foo, baz: baz},因为ES6的增强对象文字

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

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)

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

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

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

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

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

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' ]

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

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

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

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)

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

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

这是在对象数组上使用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);

解构并从对象数组中获取特定属性:

const customerList = dealerUserData?.partyDetails.map(({ partyId, custAccountId }) => ({partyId,custAccountId,customerId: dealerUserData?._id,userId: dealerUserData?.authUserID,}),);

如果您甚至不知道您正在使用的对象的确切属性,我只会改进其中一个答案:

let result = objArray.map(a => a[Object.getOwnPropertyNames(a)]);

从对象数组中,使用for循环将属性的值提取为数组。

//inputobjArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
//Codelet output=[];for(let item of objArray){output.push(item.foo);}
// Output[ 1, 3, 5 ]