41

Given input:

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

How to return:

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

For arrays it's not a problem with lodash but here we have array of objects.

Pavan Ravipati
  • 1,882
  • 13
  • 21
Szymon Toda
  • 4,195
  • 11
  • 40
  • 61
  • Is it even ok to ask on SO for npm packages? – Szymon Toda Jun 30 '15 at 10:59
  • No. You can describe the problem you're having, like you just did, and if the answer happens to be "install this npm package" then that's fine; but you shouldn't specifically ask for a package to solve the problem. – JJJ Jun 30 '15 at 11:07

9 Answers9

84

Use Object.assign:

let merged = Object.assign(...arr); // ES6 (2015) syntax

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

Note that Object.assign is not yet implemented in many environment and you might need to polyfill it (either with core-js, another polyfill or using the polyfill on MDN).

You mentioned lodash, so it's worth pointing out it comes with a _.assign function for this purpose that does the same thing:

 var merged = _.assign.apply(_, [{ a: 1 }, { b: 2 }, { c: 3 }]);

But I really recommend the new standard library way.

Benjamin Gruenbaum
  • 260,410
  • 85
  • 489
  • 489
  • 15
    Shouldn't you do `Object.assign({}, ...arr)`?! – Bergi Jun 30 '15 at 13:28
  • 1
    @Bergi I can if I want to preserve the input of `assign` intact. I certainly don't have to if I don't. Since that was not a requirement I didn't treat it as such. – Benjamin Gruenbaum Jun 30 '15 at 13:30
  • 5
    @BenjaminGruenbaum: Yes, preserving the input is a best practise :-) It can lead to ugly bugs when it's unexpected. – Bergi Jun 30 '15 at 13:33
  • 4
    My linter throws a `Expected at least 1 arguments, but got 0 or more.` if I didn't add the extra {}; `Object.assign({}, ...ids`. – Richard Fernandez Nov 08 '19 at 10:34
5

With lodash, you can use merge():

var arr = [ { a: 1 }, { b: 2 }, { c: 3 } ];
_.merge.apply(null, [{}].concat(arr));
// → { a: 1, b: 2, c: 3 }

If you're doing this in several places, you can make merge() a little more elegant by using partial() and spread():

var merge = _.spread(_.partial(_.merge, {}));
merge(arr);
// → { a: 1, b: 2, c: 3 }
Adam Boduch
  • 10,427
  • 3
  • 27
  • 38
4

Here is a version not using ES6 methods...

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/

Michael Coxon
  • 5,025
  • 1
  • 23
  • 49
  • 2
    Note that this will also copy enumerable prototype properties (which is not a problem in this particular case). – Benjamin Gruenbaum Jun 30 '15 at 11:08
  • would a simple ` && !o.propertyIsEnumerable(key)` added to the if statement fix this? – Michael Coxon Jun 30 '15 at 11:12
  • Well, for... in only enumerates enumerable properties. A `Object(o).hasOwnProperty(obj)` would fix it though (ensuring it's not on the prototype) - the `Object(o)` part is to ensure we're not calling a non-existing method so that for example passing `4` would not fail (and copy nothing). – Benjamin Gruenbaum Jun 30 '15 at 11:13
  • Aren't the enumerable properties the ones you would want to copy? For instance, you would not want something like `Array.prototype.length` in the object.. right? Or am I missing this entirely? – Michael Coxon Jun 30 '15 at 11:21
  • @BenjaminGruenbaum: Even better: `Object.prototype.hasOwnProperty.call(o, i)` to ensure we're not calling a custom method or even non-function property value. – Bergi Jun 30 '15 at 13:32
4

You can use underscore.extend function like that:

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

And to prevent modifying original array you should use

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

Vladimir
  • 322
  • 1
  • 8
2

Adding to the accepted answer, a running code snippet with 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))
Jafar Karuthedath
  • 2,587
  • 23
  • 19
1

I've got a neat little solution not requiring a polyfill.

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

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

Hope that helps :)

jonny
  • 2,938
  • 1
  • 16
  • 30
  • 2
    I have no idea why this was upvoted since it can't doesn't work - getOwnPropertyNames returns an array. – Benjamin Gruenbaum Jun 30 '15 at 12:05
  • @BenjaminGruenbaum For this use case it works perfectly. See my fiddle: http://jsfiddle.net/dhgsbg99/ – jonny Jun 30 '15 at 12:40
  • 1
    This is just incidental since all the objects have a single property and `toString`ing the array with a single property gives the same result, try adding a property to one of the objects and see it failing: http://jsfiddle.net/zqvhceyj/ – Benjamin Gruenbaum Jun 30 '15 at 12:42
  • 1
    @BenjaminGruenbaum I understand it perfectly - I was just applying a solution to the use case. I can be more generic, but the question clearly asked "for **this** input, return **this** output", I was merely doing so. – jonny Jun 30 '15 at 12:44
  • 1
    @JonathanBrooks and that's exacly what I've asked for. :-) – Szymon Toda Jun 30 '15 at 15:14
  • 1
    @Ultra Haha! That's what I gleaned from your question; I'm glad my downvotes are unwarranted :') – jonny Jun 30 '15 at 15:16
1

Here is a nice usage of Object.assign with the array.prototype.reduce function:

let merged = arrOfObjs.reduce((accum, val) => {
  Object.assign(accum, val);
  return accum;
}, {})

This approach does not mutate the input array of objects, which could help you avoid difficult to troubleshoot problems.

deed02392
  • 4,618
  • 2
  • 28
  • 48
0

You can easily flat your object to array.

function flatten(elements) {
  return elements.reduce((result, current) => {
    return result.concat(Array.isArray(current) ? flatten(current) : current);
  }, []);
};
0

6 years after this question was asked.

Object.assign is the answer (above) I like the most.

but is this also legal ?

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