46

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,890
  • 14
  • 21
Szymon Toda
  • 4,454
  • 11
  • 43
  • 62
  • 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

12 Answers12

90

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
  • 270,886
  • 87
  • 504
  • 504
  • 20
    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
  • 6
    @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
  • 6
    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
  • @jprio No, it works only on a single level. `Object.assign` is not recursive. – Bergi Jul 18 '23 at 19:56
  • Nasty mutation without following suggestion from @Bergi -also, this answer works with an array of objects too. For googlers, this is similar to Ruby's .reduce(&:merge) – jprio Jul 19 '23 at 20:56
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
  • 11,023
  • 3
  • 30
  • 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,311
  • 1
  • 24
  • 51
  • 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
  • 342
  • 1
  • 8
4

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))
jfk
  • 4,335
  • 34
  • 27
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
  • 3,022
  • 1
  • 17
  • 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,799
  • 2
  • 31
  • 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
  • 485
  • 1
  • 9
  • 24
0

With more modern spread operator

arrOfObj.reduce( (acc, curr) => ({ ...acc, ...cur }) );
O-9
  • 1,626
  • 16
  • 15
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
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Sep 13 '22 at 16:57
0

function carParts(manufacturer, model, ...parts) {
  return { manufacturer, model, ...Object.assign(...parts) };
}
console.log(
  carParts(
    "Honda",
    "2008",
    { color: "Halogen Lights" },
    { Gears: "Automatic Gears" },
    { LED: "Android LED" },
    { LED: "Android LED1" }
  )
);

This is how i have done.