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

时间:2013-10-25 13:13:03

标签: javascript javascript-objects

我有一个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的注意事项,它介绍了如何将单个对象转换为数组。

21 个答案:

答案 0 :(得分:780)

以下是实现目标的更短途径:

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

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

您还可以查看Array.prototype.map() documentation on MDN

答案 1 :(得分:578)

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

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

在ES6兼容的JS解释器上,您可以使用arrow function来简化:

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

Documentation

答案 2 :(得分:45)

查看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在Lodash v4和4.3之间指出in the comments时,添加了一个新功能,再次提供此功能。 _.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)

答案 3 :(得分:33)

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

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

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

传统的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;});

编辑:Ops / s已于10月10日更新。 TL; DR - .map()很慢。但有时可读性比性能更有价值。

答案 4 :(得分:15)

使用Array.prototype.map

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

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

答案 5 :(得分:10)

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

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

_.map(objArray,"foo")

和下划线

_.pluck(objArray,"foo")

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

答案 6 :(得分:6)

在ES6中,您可以执行以下操作:

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

答案 7 :(得分:6)

虽然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

答案 8 :(得分:5)

如果您有嵌套数组,则可以像这样:

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)

答案 9 :(得分:4)

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

a b c

结果:

hex()

答案 10 :(得分:3)

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

其他答案指出地图的使用,这是很自然的(特别是对于习惯于功能风格的人而言)和简洁。我强烈建议使用它(如果你不打扰几个IE8-IT人员)。因此,如果“更好”意味着“更简洁”,“可维护”,“可理解”,那么是的,它会更好。

另一方面,这种美丽并非没有额外的成本。我不是微型电脑的忠实粉丝,但我提出了一个小test here。结果是可预测的,旧的丑陋方式似乎比map函数更快。因此,如果“更好”意味着“更快”,那么不,继续保持旧学校时尚。

同样,这只是一个微观基础,绝不提倡反对使用map,这只是我的两分钱:)。

答案 11 :(得分:3)

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

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)

答案 12 :(得分:2)

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

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

let result = getFields(array, 'foo');

答案 13 :(得分:1)

在处理对象数组时,函数映射是一个不错的选择。虽然已经发布了许多好的答案,但使用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]});

https://jsfiddle.net/ohea7mgk/

答案 14 :(得分:1)

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

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

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

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

答案 15 :(得分:1)

通常,如果要外推数组中的对象值(如问题中所述),则可以使用reduce,map和array解构。

ES6

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)

等效的for in循环如下:

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

产生的输出:[“单词”,“再次”,“某些”,“ 1”,“ 2”,“ 3”]

答案 16 :(得分:1)

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

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 :(得分:1)

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

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

答案 18 :(得分:0)

以上提供的答案非常适合提取单个属性,如果要从对象数组中提取多个属性,该怎么办。 这是解决方案!! 在这种情况下,我们可以简单地使用_.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}]

享受!!!

答案 19 :(得分:0)

如果您想在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}等效。

答案 20 :(得分:0)

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