potato83
potato83

Reputation: 71

Merge arrays of objects into one array of objects

OK so I've already actually figured out a solution to my problem, but it's ugly, and I'm sure there's a much more elegant way to do it.

Say I've got two arrays (which I know to be of the same length), of simple objects, like this:

var aVals = [{a: 1}, {a: 2}, {a: 3}];

var bVals = [{b: 4}, {b: 5}, {b: 6}];

What I want to do is squash these two arrays together, so to speak, to get something that looks like this:

var allVals = [{a: 1, b: 4},{a: 2, b: 5},{a: 3, b: 6}];

Granted, I can do that like this:

var uglySolution = [];

for(var i = 0; i < aVals.length; i++){
  var temp = [];
  temp.push(aVals[i]);
  temp.push(bVals[i]);
  uglySolution.push(Object.assign({}, ...temp)); 
}

console.log(uglySolution);

But there's got to be a better way! Right?

*Bonus quest: and what if I couldn't be sure both arrays were the same length?

Upvotes: 1

Views: 101

Answers (9)

Ele
Ele

Reputation: 33726

You can use the function Array.from.

  • Check for the max length.
  • Check for object at a specific index for both arrays.

var aVals = [{a: 1}, {a: 2}, {a: 3}];
var bVals = [{b: 4}, {b: 5}, {b: 6}, {a: 4}];

var merge = Array.from({length: Math.max(aVals.length, bVals.length)}, (_, i) => {
  return Object.assign({}, bVals[i] || {}, aVals[i] || {});
});

console.log(merge)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 2

Nina Scholz
Nina Scholz

Reputation: 386520

You could collect all arrays with the data and use an approach which works for an arbitrary count of given arrays with objects and length.

var merge = (r, a) => (a.forEach((o, i) => Object.assign(r[i] = r[i] || {}, o)), r)
    aVals = [{ a: 1 }, { a: 2 }, { a: 3 }],
    bVals = [{ b: 4 }, { b: 5 }, { b: 6 }],
    result = [aVals, bVals].reduce(merge, []);
    
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 0

Naga Sai A
Naga Sai A

Reputation: 10975

To achieve expected result use forEach method with array destructuring and it works for any array length

var aVals = [{a: 1}, {a: 2}];
var bVals = [{b: 4}, {b: 5}, {b: 6}];
var allVals = []

var func = (arr) => arr.forEach(function(element,index) {
  allVals[index] = {...allVals[index],...arr[index]}
});

func(aVals);
func(bVals);
console.log(allVals)

code sample - https://codepen.io/pen/?editors=1010

Upvotes: 0

CRice
CRice

Reputation: 32146

I think your solution is fine. It works and it's readable, so I'd say there's no need for a "better solution". However, if you're looking for something shorter, you can definitely compress your code a bit.

Here's one way of doing so that uses array destructuring and object spread. Just take the longer array, and map each of it's items to the combo of that item and the item in the same position in the other array.

var aVals = [{a: 1}, {a: 2}, {a: 3}];
var bVals = [{b: 4}, {b: 5}, {b: 6}, {b: 7}];

// var allVals = [{a: 1, b: 4},{a: 2, b: 5},{a: 3, b: 6}];

function zipArrays(arr1, arr2) {
    // Swap if arr2 is longer, so that on the next line, arr1 is always longer
    if (arr2.length > arr1.length) [arr1, arr2] = [arr2, arr1];
    return arr1.map((o, i) => ({...o, ...arr2[i]}));
}

console.log(zipArrays(aVals, bVals))

Upvotes: 0

Sreekanth
Sreekanth

Reputation: 3130

You could use ES6 and do something like this.

var aVals = [{
  a: 1
}, {
  a: 2
}, {
  a: 3
}];

var bVals = [{
  b: 4
}, {
  b: 5
}, {
  b: 6
}, {
  b: 7
}];

const mergeArrays = (obj1, obj2) => {
  if (obj1.length > obj2.length) {
    [obj1, obj2] = [obj2, obj1];
  }
  const output = obj1.map((value, index) => {
    return {
      ...value,
      ...obj2[index]
    };
  });
  return output.concat(obj2.slice(obj1.length, obj2.length));
}

console.log(mergeArrays(aVals, bVals));

Upvotes: 0

Nicholas Siegmundt
Nicholas Siegmundt

Reputation: 869

You could one line what's in the for loop like this:

var uglySolution = [];

for(var i = 0; i < aVals.length; i++){
  uglySolution.push(Object.assign({}, aVals[i], bVals[i]))
}

console.log(uglySolution);

Assuming both arrays are the same size.

If they are NOT the same size, do this:

var uglySolution = [];
var longestLength =  aVals.length >  bVals.length ? aVals.length : bVals.length;

for(var i = 0; i < longestLength; i++){
  uglySolution.push(Object.assign({}, aVals[i] || {} , bVals[i] || {}))
}

console.log(uglySolution);

Upvotes: 0

AlexScr
AlexScr

Reputation: 452

If you have arrays of the same length it's simple:

var aVals = [{a: 1}, {a: 2}, {a: 3}];
var bVals = [{b: 4}, {b: 5}, {b: 6}];
var result = aVals.map((el, i) => Object.assign({}, el, bVals[i]));

console.log(result);

To do it with different array length I'd do it like this:

var aVals = [{a: 1}, {a: 2}];
var bVals = [{b: 4}, {b: 5}, {b: 6}];
var result = [];
var length = Math.max(aVals.length, bVals.length);

for (var i = 0; i < length; i++) {
  result.push(Object.assign({}, aVals[i] || {}, bVals[i] || {}));
}

console.log(result);

Upvotes: 2

Kraylog
Kraylog

Reputation: 7553

This would be a bit better, I think.

var aVals = [{a: 1}, {a: 2}, {a: 3}];

var bVals = [{b: 4}, {b: 5}, {b: 6}];

var result = aVals.map((val, index) => Object.assign(val, bVals[index]));

console.log(result);

Upvotes: 0

You can get rid of the temp var and the uglySolution var.

for (var i = 0; i < bVals.length; i++) {
    aVals.push(bVals.pop())
}
console.log(aVals);

This simplifies it into one var being used and only a small loop.

Upvotes: 0

Related Questions