1542

我有具有以下结构的 JavaScript 对象数组:

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

我想从每个对象中提取一个字段,并获取一个包含值的数组,例如 fieldfoo将给出 array [ 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 ]

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


注意建议的 duplicate,它涵盖了如何将单个对象转换为数组。

4

22 回答 22

2125

这是实现它的更短的方法:

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

或者

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

你也可以检查Array.prototype.map()

于 2017-10-11T17:33:48.910 回答
686

是的,但它依赖于 JavaScript 的 ES5 特性。这意味着它不适用于 IE8 或更早版本。

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

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

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

Array.prototype.map 文档

于 2013-10-25T13:14:56.760 回答
65

说到仅 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 Ops/sec

var vals=[];
for(var item of testArray){
   vals.push(item.val); 
}

Array.prototype.map 19 操作/秒

var vals = testArray.map(function(a) {return a.val;});

TL;DR - .map() 很慢,但如果您觉得可读性比性能更重要,请随意使用它。

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

于 2017-02-06T02:13:48.100 回答
56

查看Lodash 的_.pluck()函数或Underscore_.pluck()函数。两者都在一个函数调用中完全满足您的要求!

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

更新: _.pluck()自 Lodash v4.0.0 起已被删除,有利于_.map()与类似于Niet's answer的内容结合使用。_.pluck()在 Underscore 中仍然可用

更新 2:正如 Mark在评论中指出的那样,在 Lodash v4 和 4.3 之间的某个地方,添加了一个新功能,再次提供此功能。_.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 的 R.pluck()函数,它看起来像这样:

var result = R.pluck('foo')(objArray);  // or just R.pluck('foo', objArray)
于 2015-04-16T03:16:44.163 回答
23

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

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

_.map(objArray,"foo")

和下划线

_.pluck(objArray,"foo")

两者都将返回

[1, 2, 3]
于 2016-02-09T04:00:04.260 回答
18

使用Array.prototype.map

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

有关 ES5 之前的浏览器的 shim,请参阅上面的链接。

于 2013-10-25T13:15:16.520 回答
18

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

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' ]
于 2020-09-28T21:48:27.947 回答
17

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"]
于 2021-08-04T18:41:53.407 回答
16

在 ES6 中,你可以这样做:

const objArray = [{foo: 1, bar: 2}, {foo: 3, bar: 4}, {foo: 5, bar: 6}]
objArray.map(({ foo }) => foo)
于 2018-07-10T17:36:47.557 回答
8

通常,如果您想推断数组内的对象值(如问题中所述),那么您可以使用 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 in循环的等价物是:

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

于 2018-07-14T09:27:28.743 回答
8

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

即使提供的列表中的一项不是对象或不包含该字段,这也将起作用。

如果项目不是对象或不包含字段,它甚至可以通过协商默认值来变得更加灵活。

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 相同,因为返回数组的长度与提供的数组相同。(在这种情况下 amap比 a 稍微便宜一些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 示例

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

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 示例

于 2016-11-19T10:02:14.320 回答
7

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

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

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

let arrayLike = new Set([{foo: 1}, {foo: 2}, {foo: 3}]);
于 2018-04-06T14:16:08.167 回答
7

如果您想要 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 的增强的对象文字

于 2019-04-27T14:17:07.037 回答
7

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

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)

于 2020-05-11T00:55:48.827 回答
6

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

其他答案指出了地图的使用,这很自然(特别是对于习惯于功能风格的人)和简洁。我强烈推荐使用它(如果你不介意少数 IE8-IT 人员的话)。因此,如果“更好”意味着“更简洁”、“可维护”、“可理解”,那么是的,它会更好。

另一方面,这种美丽并非没有额外的成本。我不是 microbench 的忠实粉丝,但我在这里做了一个小测试。结果可想而知,旧的丑陋方式似乎比地图功能更快。因此,如果“更好”意味着“更快”,那么不,请保持老派时尚。

同样,这只是一个微型工作台,绝不反对使用map,这只是我的两分钱 :)。

于 2013-10-25T13:54:03.770 回答
6

上面的答案适用于单个属性,但是当从数组中选择多个属性时使用这个

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)
于 2021-03-27T12:04:37.857 回答
5

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

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

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

于 2020-08-13T18:26:42.637 回答
4

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

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

let result = getFields(array, 'foo');
于 2020-12-26T10:59:10.147 回答
3

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

请享用!!!

于 2019-02-06T06:34:09.943 回答
3

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

 let objArray2 = [];
 objArray.forEach(arr => objArray2.push(arr.foo));
于 2021-05-18T15:12:03.423 回答
2

在处理对象数组时,函数映射是一个不错的选择。尽管已经发布了许多好的答案,但使用 map 与 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/

于 2015-11-28T08:32:50.530 回答
1

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

于 2021-12-23T17:21:40.993 回答