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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19590865/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 16:05:50  来源:igfitidea点击:

From an array of objects, extract value of a property as array

javascriptjavascript-objects

提问by hyde

I have JavaScript object array with the following structure:

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

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

I want to extract a field from each object, and get an array containing the values, for example field foowould give array [ 1, 3, 5 ].

我想从每个对象中提取一个字段,并获取一个包含值的数组,例如 fieldfoo会给出 array [ 1, 3, 5 ]

I can do this with this trivial approach:

我可以用这种微不足道的方法做到这一点:

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 ]

Is there a more elegant or idiomatic way to do this, so that a custom utility function would be unnecessary?

是否有更优雅或惯用的方法来执行此操作,以便不需要自定义实用程序函数?



Note about suggested duplicate, it covers how to convert a single objectto an array.

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

回答by Jalasem

Here is a shorter way of achieving it:

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

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

OR

或者

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

You can also check Array.prototype.map().

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

回答by Niet the Dark Absol

Yes, but it relies on an ES5 feature of JavaScript. This means it will not work in IE8 or older.

是的,但它依赖于 JavaScript 的 ES5 特性。这意味着它不能在 IE8 或更旧的版本中工作。

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

On ES6 compatible JS interpreters you can use an arrow functionfor brevity:

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

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

Array.prototype.map documentation

Array.prototype.map 文档

回答by cpdt

Check out Lodash's _.pluck()function or Underscore's _.pluck()function. Both do exactly what you want in a single function call!

查看Lodash 的_.pluck()函数或Underscore_.pluck()函数。两者都可以在单个函数调用中完成您想要的操作!

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

Update:_.pluck()has been removed as of Lodash v4.0.0, in favour of _.map()in combination with something similar to Niet's answer. _.pluck()is still available in Underscore.

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

Update 2:As Mark points out in the comments, somewhere between Lodash v4 and 4.3, a new function has been added that provides this functionality again. _.property()is a shorthand function that returns a function for getting the value of a property in an object.

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

Additionally, _.map()now allows a string to be passed in as the second parameter, which is passed into _.property(). As a result, the following two lines are equivalent to the code sample above from pre-Lodash 4.

此外,_.map()现在允许将字符串作为第二个参数传入,该参数传入_.property(). 因此,以下两行代码等效于上述 Lodash 4 之前的代码示例。

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

_.property(), and hence _.map(), also allow you to provide a dot-separated string or array in order to access sub-properties:

_.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']));

Both _.map()calls in the above example will return [5, 2, 9].

_.map()上面示例中的两个调用都将返回[5, 2, 9].

If you're a little more into functional programming, take a look at Ramda'sR.pluck()function, which would look something like this:

如果您更喜欢函数式编程,请查看Ramda 的R.pluck()函数,它看起来像这样:

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

回答by pscl

Speaking for the JS only solutions, I've found that, inelegant as it may be, a simple indexed forloop is more performant than its alternatives.

说到 JS 唯一的解决方案,我发现,尽管可能不优雅,但简单的索引for循环比它的替代方案更高效。

Extracting single property from a 100000 element array (via jsPerf)

从 100000 个元素的数组中提取单个属性(通过 jsPerf)

Traditional for loop368 Ops/sec

传统 for 循环368 Ops/sec

var vals=[];
for(var i=0;i<testArray.length;i++){
   vals.push(testArray[i].val);
}

ES6 for..of loop303 Ops/sec

ES6 for..of 循环303 Ops/sec

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

Array.prototype.map19 Ops/sec

Array.prototype.map19 次操作/秒

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

TL;DR - .map() is slow, but feel free to use it if you feel readability is worth more than performance.

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

Edit #2: 6/2019 - jsPerf link broken, removed.

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

回答by Tejas Patel

It is better to use some sort of libraries like lodash or underscore for cross browser assurance.

最好使用诸如 lodash 或 underscore 之类的库来确保跨浏览器。

In Lodash you can get values of a property in array by following method

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

_.map(objArray,"foo")

and in Underscore

并在下划线

_.pluck(objArray,"foo")

Both will return

两者都会回来

[1, 2, 3]

回答by Alnitak

Using Array.prototype.map:

使用Array.prototype.map

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

See the above link for a shim for pre-ES5 browsers.

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

回答by Yuxuan Chen

In ES6, you can do:

在 ES6 中,你可以这样做:

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

回答by Eugen Sunic

In general, if you want to extrapolate object values which are inside an array (like described in the question) then you could use reduce, map and array destructuring.

通常,如果您想推断数组内的对象值(如问题中所述),那么您可以使用reduce、map 和数组解构。

ES6

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)

The equivalent using for inloop would be:

等效的使用for in循环是:

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

Produced output:["word", "again", "some", "1", "2", "3"]

产生的输出:["word", "again", "some", "1", "2", "3"]

回答by Rogier Spieker

While mapis a proper solution to select 'columns' from a list of objects, it has a downside. If not explicitly checked whether or not the columns exists, it'll throw an error and (at best) provide you with undefined. I'd opt for a reducesolution, which can simply ignore the property or even set you up with a default value.

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

jsbin 示例

This would work even if one of the items in the provided list is not an object or does not contain the field.

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

It can even be made more flexible by negotiating a default value should an item not be an object or not contain the field.

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

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

jsbin 示例

This would be the same with map, as the length of the returned array would be the same as the provided array. (In which case a mapis slightly cheaper than a reduce):

这与 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 example

jsbin 示例

And then there is the most flexible solution, one which lets you switch between both behaviours simply by providing an alternative value.

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

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

jsbin 示例

As the examples above (hopefully) shed some light on the way this works, lets shorten the function a bit by utilising the Array.concatfunction.

由于上面的示例(希望)阐明了它的工作方式,让我们通过使用该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

jsbin 示例

回答by sitifensys

It depends of your definition of "better".

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

The other answers point out the use of map, which is natural (especially for guys used to functional style) and concise. I strongly recommend using it (if you don't bother with the few IE8- IT guys). So if "better" means "more concise", "maintainable", "understandable" then yes, it's way better.

其他答案指出了map的使用,自然(尤其对于习惯函数式风格的人),简洁。我强烈推荐使用它(如果你不打扰少数 IE8-IT 人员的话)。因此,如果“更好”的意思是“更简洁”、“可维护”、“可理解”,那么是的,它会更好。

In the other hand, this beauty don't come without additional costs. I'm not a big fan of microbench, but I've put up a small test here. The result are predictable, the old ugly way seems to be faster than the map function. So if "better" means "faster", then no, stay with the old school fashion.

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

Again this is just a microbench and in no way advocating against the use of map, it's just my two cents :).

同样,这只是一个微型平台,绝不提倡使用map,这只是我的两分钱:)。