将包含对象的数组展开成一个对象

46

给定输入:

[{ a: 1 }, { b: 2 }, { c: 3 }]

如何退货:

{ a: 1, b: 2, c: 3 }

对于数组来说,使用 lodash 并不是问题,但这里我们有一个对象数组。


在SO上询问npm包是否合适? - Szymon Toda
不可以直接询问一个包来解决问题,但你可以描述你遇到的问题,就像你刚才所做的那样,如果答案恰好是“安装这个npm包”,那么没问题。 - JJJ
12个回答

91
使用Object.assign
let merged = Object.assign({}, ...arr); // ES6 (2015) syntax

var merged = Object.assign.apply(Object, arr); // ES5 syntax

请注意,Object.assign在许多环境中尚未实现,您可能需要使用polyfill(可以使用core-js、其他polyfill或在MDN上使用polyfill)来填充它。
您提到了lodash,值得指出的是它提供了一个_.assign函数,用于实现相同的功能。
 var merged = _.assign.apply(_, [{ a: 1 }, { b: 2 }, { c: 3 }]);

但是我真的推荐使用新的标准库方法。

20
你不应该使用Object.assign({}, ...arr)吗?! - Bergi
1
@Bergi 如果我想保留“assign”的输入不变,我就可以这样做。如果我不想这样做,我当然不必这样做。既然那不是一个要求,我也没有把它当作一个要求来处理。 - Benjamin Gruenbaum
6
@BenjaminGruenbaum: 是的,保留输入是最佳实践 :-) 当它出乎意料时会导致不美观的错误。 - Bergi
6
如果我没有添加额外的{},我的lint程序会抛出“Expected at least 1 arguments, but got 0 or more.”。即 Object.assign({}, ...ids - Richard Fernandez
@jprio 不,它只在一个层级上起作用。Object.assign 不具备递归功能。 - Bergi
没有遵循 @Bergi 的建议,这个恶心的变异也适用于对象数组。对于谷歌用户来说,这类似于 Ruby 的 .reduce(&:merge)。 - jprio

5
使用lodash,您可以使用merge()
var arr = [ { a: 1 }, { b: 2 }, { c: 3 } ];
_.merge.apply(null, [{}].concat(arr));
// → { a: 1, b: 2, c: 3 }

如果您在多个地方这样做,您可以使用partial()spread()使merge()更加优雅:
var merge = _.spread(_.partial(_.merge, {}));
merge(arr);
// → { a: 1, b: 2, c: 3 }

4

以下是不使用ES6方法的版本...

var arr = [{ a: 1 }, { b: 2 }, { c: 3 }];
var obj = {};

for(var i = 0; i < arr.length; i++) {
    var o = arr[i];
    for(var key in o) {
        if(typeof o[key] != 'function'){
            obj[key] = o[key];
        }
    }
}

console.log(obj);

fiddle: http://jsfiddle.net/yaw3wbb8/


2
请注意,这也会复制可枚举的原型属性(在这种特定情况下不是问题)。 - Benjamin Gruenbaum
在if语句中加入一个简单的&& !o.propertyIsEnumerable(key)是否可以解决这个问题? - Michael Coxon
好的,对于...in语句只枚举可枚举属性。但是Object(o).hasOwnProperty(obj)可以解决这个问题(确保它不在原型上)- Object(o)部分是为了确保我们不调用不存在的方法,以便例如传递4不会失败(并且不复制任何内容)。 - Benjamin Gruenbaum
可枚举属性不是您想要复制的属性吗?例如,您不希望在对象中出现像Array.prototype.length这样的东西,对吧?或者我完全误解了吗? - Michael Coxon
@BenjaminGruenbaum: 更好的方法是:Object.prototype.hasOwnProperty.call(o, i),以确保我们不会调用自定义方法或非函数属性值。 - Bergi

4
您可以像这样使用underscore.extend函数:
var _ = require('underscore');
var a = [{ a: 1 }, { b: 2 }, { c: 3 }];

var result = _.extend.apply(null, a);
console.log(result); // { a: 1, b: 2, c: 3 }
console.log(a); // [ { a: 1, b: 2, c: 3 }, { b: 2 }, { c: 3 } ]

为了避免修改原始数组,您应该使用

var _ = require('underscore');
var a = [{ a: 1 }, { b: 2 }, { c: 3 }];

var result = _.extend.apply(null, [{}].concat(a));
console.log(result); // { a: 1, b: 2, c: 3 }
console.log(a); // [ { a: 1 }, { b: 2 }, { c: 3 } ]

Here can test it


4

补充接受的答案,提供一个使用ES6的运行代码片段。

let input = [{ a: 1 }, { b: 2 }, { c: 3 }]
//Get input object list with spread operator
console.log(...input)
//Get all elements in one object
console.log(Object.assign(...input))


1
我有一个不需要polyfill的简单解决方案。
var arr = [{ a: 1 }, { b: 2 }, { c: 3 }];
var object = {};

arr.map(function(obj){
    var prop = Object.getOwnPropertyNames(obj);
    object[prop] = obj[prop];
});

希望这有所帮助 :)

2
我不知道为什么这个被点赞了,因为它不能工作 - getOwnPropertyNames返回一个数组。 - Benjamin Gruenbaum
@BenjaminGruenbaum 对于这个用例,它完美地工作。请查看我的fiddle:http://jsfiddle.net/dhgsbg99/ - jonny
1
这只是附带的,因为所有对象都只有一个属性,使用具有单个属性的数组进行toString会给出相同的结果,尝试向其中一个对象添加属性并看到它失败:http://jsfiddle.net/zqvhceyj/ - Benjamin Gruenbaum
1
@BenjaminGruenbaum 我完全理解它 - 我只是为使用案例应用了一个解决方案。我可以更通用,但问题明确要求“对于这个输入,返回这个输出”,我只是在这么做。 - jonny
1
@JonathanBrooks,这正是我所要求的。 :-) - Szymon Toda
1
@Ultra 哈哈!这就是我从你的问题中得出的结论;我很高兴我的踩票是没有根据的 :') - jonny

1
这里是使用 Object.assignarray.prototype.reduce 函数的好用法例子:
let merged = arrOfObjs.reduce((accum, val) => {
  Object.assign(accum, val);
  return accum;
}, {})

这种方法不会改变输入的对象数组,这有助于避免难以排查的问题。

1
转换为其他类型的对象时非常有用。 - Michal.S

0

这个问题被提出6年后。

Object.assign是我最喜欢的答案(如上所述)。

但这也合法吗?

let res = {};
[{ a: 1 }, { b: 2 }, { c: 3 }].forEach(val => {
    let key = Object.keys(val);
    console.log(key[0]);
    res[key] = val[key];
})

0

使用更现代的扩展运算符

arrOfObj.reduce( (acc, curr) => ({ ...acc, ...cur }) );

0
const data = [
  [{ a: "a" }, { b: "b" }, { c: "c" }],
  [{ d: "d" }, { e: "e" }, { f: "f" }],
  [{ g: "g" }, { h: "h" }, { i: "i" }],
];

function convertToObject(array){
  const response = {};
  for (let i = 0; i < array.length; i++) {
    const innerArray = array[i];
    for (let i = 0; i < innerArray.length; i++) {
      const object = innerArray[i];
      const keys = Object.keys(object);
      for (let j = 0; j < keys.length; j++) {
        const key = keys[j];
        response[key] = object[key];
      }
    }
  }
  return response;
}

console.log(convertToObject(data));

1
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接