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

hyd*_*yde 851 javascript javascript-objects

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

objArray = [ { foo: 1, bar: 2}, { foo: 3, bar: 4}, { foo: 5, bar: 6} ];
Run Code Online (Sandbox Code Playgroud)

我想提取一个包含key值的数组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 ]
Run Code Online (Sandbox Code Playgroud)

有没有更优雅和高效的方法来实现?


关于建议重复的注意事项,该问题询问如何将对象转换数组,这个问题询问如何对象数组中提取单个属性.

Jal*_*sem 1011

这是实现它的一种较短的方法:

Array.prototype.map()

要么

Array.prototype.map()

您还可以在MDN上检查Array.prototype.map()文档

  • @Alnitak使用更新的功能,在我看来,**客观上更好.这个片段非常常见,所以我不相信这是抄袭.保持过时的答案固定在顶部并没有任何价值. (22认同)
  • `=>`符号仅在ES6上可用(ECMASCRIPT 6) (19认同)
  • @PauloRoberto Arrow函数[除IE以外的所有地方都基本支持。](https://caniuse.com/#feat=arrow-functions) (4认同)
  • 当然,这是允许的,但恕我直言,没有什么能让这个答案客观上更好,除了它使用的语法在您提出问题时_不可用_并且在某些浏览器中甚至不受支持。我还注意到,这个答案是在这个答案发布前近一年对最初接受的答案所做评论的直接副本。 (4认同)
  • 注释不是答案,imo如果有人发表评论而不是答案,那是他们自己的错,如果有人将其复制到答案中。 (4认同)
  • 嗯,这和totymedli的另一个答案的评论是一样的,但是,实际上(在我看来)它比其他*答案*更好,所以......把它改成接受的答案. (2认同)
  • 我真的很喜欢那个,顺便说一句,那个“=&gt;”符号对我来说闻起来像是新事物,所以我认为如果该解决方案实际上可行,则需要对其兼容性进行审查。 (2认同)

Nie*_*sol 598

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

var result = objArray.map(function(a) {return a.foo;});
Run Code Online (Sandbox Code Playgroud)

在ES6兼容的JS解释器上,您可以使用箭头功能以简化:

var result = objArray.map(a => a.foo);
Run Code Online (Sandbox Code Playgroud)

文档

  • @Black更好:`var result = objArray.map(a => a.foo);` (28认同)
  • 这个解决方案看起来简洁明了,但它比普通的循环方法更优化. (4认同)
  • @FaizKhan注意这一年.这个答案是在2013年10月25日发布的."接受"的答案是在2017年10月11日发布的.如果有的话,那个复选标记就转到了另一个. (4认同)
  • 在ES6中你可以做`var result = objArray.map((a)=>(a.foo));` (2认同)

cpd*_*pdt 50

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

var result = _.pluck(objArray, 'foo');
Run Code Online (Sandbox Code Playgroud)

更新: _.pluck()已从Lodash v4.0.0中删除,有利于_.map()Niet的答案类似的组合._.pluck()仍然可以在Underscore中使用.

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

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

var result = _.map(objArray, 'foo');
var result = _.map(objArray, _.property('foo'));
Run Code Online (Sandbox Code Playgroud)

_.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']));
Run Code Online (Sandbox Code Playgroud)

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

如果您对函数式编程有更多了解,请查看Ramda的 R.pluck()函数,它看起来像这样:

var result = R.pluck('foo')(objArray);  // or just R.pluck('foo', objArray)
Run Code Online (Sandbox Code Playgroud)

  • 好消息:介于Lodash 4.0.0和4.3.0之间`_​​.property('foo')`(https://lodash.com/docs#property)被添加为`function(o){return o}的简写包含.foo; }`.这将Lodash用例缩短为`var result = _.pluck(objArray,_.property('foo'));`为了更方便,Lodash 4.3.0的`_.map()`[方法](https:/ /lodash.com/docs#map)也允许在引擎盖下使用`_.property()`的简写,导致`var result = _.map(objArray,'foo');` (4认同)

psc*_*scl 37

对于仅有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);
}
Run Code Online (Sandbox Code Playgroud)

ES6 for..of循环 303 Ops/sec

var vals=[];
for(var item of testArray){
   vals.push(item.val); 
}
Run Code Online (Sandbox Code Playgroud)

Array.prototype.map 19 Ops/sec

var vals = testArray.map(function(a) {return a.val;});
Run Code Online (Sandbox Code Playgroud)

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

  • 我刚刚用 [jsBench](https://jsbench.me/) 重新测试了它(非常感谢 [Mirko Vukušić](/sf/answers/2952670521/) 创建了 jsPerf 的替代方案),结果仍然[相同](https://jsbench.me/t2kuxn4ha4/3) - `for` 是最快的,`for .. of` 稍慢,`map` 大约慢一半。 (4认同)

Aln*_*tak 15

使用Array.prototype.map:

function getFields(input, field) {
    return input.map(function(o) {
        return o[field];
    });
}
Run Code Online (Sandbox Code Playgroud)

有关预先ES5浏览器的垫片,请参阅上面的链接.


Tej*_*tel 13

最好使用某种类型的库,如lodash或下划线来实现跨浏览器保证.

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

_.map(objArray,"foo")
Run Code Online (Sandbox Code Playgroud)

并以下划线

_.pluck(objArray,"foo")
Run Code Online (Sandbox Code Playgroud)

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


Abi*_*lah 13

我知道已经提供了很多答案并且涵盖了广泛的解决方案。

但我想添加一些我在上述所有答案中都没有找到的内容。

例如我将使用以下类型的数据

const array = [
   {name:"foo",age:23, skills:["reactjs","nodejs","nextjs"]},
   {name:"bar",age:25, skills:["angular","java"]}
]
Run Code Online (Sandbox Code Playgroud)

如果您要从对象数组的所有索引中提取单个属性,那么只使用映射函数就可以了,正如许多社区成员已经回答的那样,作为参考,我将在此处编写代码:

array.map(a => a.skills);
Run Code Online (Sandbox Code Playgroud)

上面的代码片段将生成长度为 2 的数组,这意味着它将返回两个索引的技能字段,


因此,如果您想从数组对象中获取单个字段特定的字段,例如仅 bar 的技能,这里是通过组合过滤器和映射函数来实现的示例代码

const array = [
   {name:"foo",age:23, skills:["reactjs","nodejs","nextjs"]},
   {name:"bar",age:25, skills:["angular","java"]}
]
Run Code Online (Sandbox Code Playgroud)


最后,如果你想要一个完全动态/通用的代码,那就是

array.map(a => a.skills);
Run Code Online (Sandbox Code Playgroud)


jaf*_*mlp 10

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

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);
Run Code Online (Sandbox Code Playgroud)

结果 :

[ 1, 2 ]
[ 'name1', 'name2' ]
[ 'value1', 'value2' ]
Run Code Online (Sandbox Code Playgroud)


小智 10

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

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'}]
Run Code Online (Sandbox Code Playgroud)

现在我只选择两个字段

 var outPutArray=arrayObj.map(( {Name,Email} ) =>  ({Name,Email}) )
 console.log(outPutArray)
Run Code Online (Sandbox Code Playgroud)


Yux*_*hen 9

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

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


Eug*_*nic 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)
Run Code Online (Sandbox Code Playgroud)

使用for in循环的等效方法是:

for (let i in a) {
  let temp = [];
  for (let j in a[i]) {
    temp.push(a[i][j]);
  }
  array.push(temp);
}
Run Code Online (Sandbox Code Playgroud)

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


Rog*_*ker 7

虽然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;
    }, []);
}
Run Code Online (Sandbox Code Playgroud)

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;
    }, []);
}
Run Code Online (Sandbox Code Playgroud)

jsbin的例子

这与map相同,因为返回的数组的长度与提供的数组相同.(在这种情况下,a map比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;
    }, []);
}
Run Code Online (Sandbox Code Playgroud)

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;
    }, []);
}
Run Code Online (Sandbox Code Playgroud)

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 : []));
    }, []);
}
Run Code Online (Sandbox Code Playgroud)

jsbin的例子


Chr*_*son 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 }))
Run Code Online (Sandbox Code Playgroud)

这就像{foo, baz}左侧使用对象析构一样工作,而箭头右侧的等效{foo: foo, baz: baz}ES6 的增强对象文字


Tec*_*dom 6

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

Array.from(arrayLike, x => x.foo);
Run Code Online (Sandbox Code Playgroud)

The advantage it has over Array.prototype.map() method is the input can also be a Set:

let arrayLike = new Set([{foo: 1}, {foo: 2}, {foo: 3}]);
Run Code Online (Sandbox Code Playgroud)


Qui*_*inn 6

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

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)
Run Code Online (Sandbox Code Playgroud)


Gav*_*son 6

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

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}));
Run Code Online (Sandbox Code Playgroud)

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

根据需要在地图功能中添加或删除属性


Kur*_*den 6

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

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

let result = getFields(array, 'foo');
Run Code Online (Sandbox Code Playgroud)


sit*_*sys 5

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

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

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

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


Qii*_*iia 5

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

 let objArray2 = [];
 objArray.forEach(arr => objArray2.push(arr.foo));
Run Code Online (Sandbox Code Playgroud)