JavaScript merging objects by id

JavascriptArraysunderscore.js

Javascript Problem Overview


What's the correct way to merge two arrays in Javascript?

I've got two arrays (for example):

var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]

I want to be able to end up with something like:

var a3 = [{ id : 1, name : "test", count : "1"}, 
          { id : 2, name : "test2", count : "2"}]

Where the two arrays are being joined based on the 'id' field and extra data is simply being added.

I tried to use _.union to do this, but it simply overwrites the values from the second array into the first one

Javascript Solutions


Solution 1 - Javascript

Short ES6 solution

const a3 = a1.map(t1 => ({...t1, ...a2.find(t2 => t2.id === t1.id)}))

Solution 2 - Javascript

This should do the trick:

var mergedList = _.map(a1, function(item){
	return _.extend(item, _.findWhere(a2, { id: item.id }));
});

This assumes that the id of the second object in a1 should be 2 rather than "2"

Solution 3 - Javascript

Assuming IDs are strings and the order does not matter, you can

  1. Create a hash table.
  2. Iterate both arrays and store the data in the hash table, indexed by the ID. If there already is some data with that ID, update it with Object.assign (ES6, can be polyfilled).
  3. Get an array with the values of the hash map.

var hash = Object.create(null);
a1.concat(a2).forEach(function(obj) {
    hash[obj.id] = Object.assign(hash[obj.id] || {}, obj);
});
var a3 = Object.keys(hash).map(function(key) {
    return hash[key];
});

In ECMAScript6, if the IDs are not necessarily strings, you can use Map:

var hash = new Map();
a1.concat(a2).forEach(function(obj) {
    hash.set(obj.id, Object.assign(hash.get(obj.id) || {}, obj))
});
var a3 = Array.from(hash.values());

Solution 4 - Javascript

ES6 simplifies this:

let merge = (obj1, obj2) => ({...obj1, ...obj2});

Note that repeated keys will be merged, and the value of the second object will prevail and the repeated value of the first object will be ignored.

Example:

let obj1 = {id: 1, uniqueObj1Key: "uniqueKeyValueObj1", repeatedKey: "obj1Val"};
let obj2 = {id: 1, uniqueObj2Key: "uniqueKeyValueObj2", repeatedKey: "obj2Val"};

merge(obj1, obj2)
// {id: 1, uniqueObj1Key: "uniqueKeyValueObj1", repeatedKey: "obj2Val", uniqueObj2Key: "uniqueKeyValueObj2"}
merge(obj2, obj1)
// {id: 1, uniqueObj2Key: "uniqueKeyValueObj2", repeatedKey: "obj1Val", uniqueObj1Key: "uniqueKeyValueObj1"}

Complete solution (with Lodash, not Underscore)

var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
var merge = (obj1, obj2) => ({...obj1, ...obj2});
_.zipWith(a1, a2, merge)
(2) [{…}, {…}]
   0: {id: 1, name: "test", count: "1"}
   1: {id: 2, name: "test2", count: "2"}

If you have an array of arrays to merge you can do it like this:

var arrayOfArraysToMerge = [a1, a2, a3, a4]; //a3 and a4 are arrays like a1 and a2 but with different properties and same IDs.
_.zipWith(...arrayOfArraysToMerge, merge)
(2) [{…}, {…}]
   0: {id: 1, name: "test", count: "1", extra1: "val1", extra2: 1}
   1: {id: 2, name: "test2", count: "2", extra1: "val2", extra2: 2}

Solution 5 - Javascript

reduce version.

var a3 = a1.concat(a2).reduce((acc, x) => {
    acc[x.id] = Object.assign(acc[x.id] || {}, x);
    return acc;
}, {});
_.values(a3);

I think it's common practice in functional language.

Solution 6 - Javascript

Already there are many great answers, I'll just add another one which is from a real problem I needed to solve yesterday.

I had an array of messages with user ids, and one array of users containing users' names and other details. This is how I managed to add user details to the messages.

var messages = [{userId: 2, content: "Salam"}, {userId: 5, content: "Hello"},{userId: 4, content: "Moi"}];
var users = [{id: 2, name: "Grace"}, {id: 4, name: "Janetta"},{id: 5, name: "Sara"}];

var messagesWithUserNames = messages.map((msg)=> {
  var haveEqualId = (user) => user.id === msg.userId
  var userWithEqualId= users.find(haveEqualId)
  return Object.assign({}, msg, userWithEqualId)
})
console.log(messagesWithUserNames)

Solution 7 - Javascript

Vanilla JS solution

const a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
const a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]

const merge = (arr1, arr2) => {
  const temp = []

  arr1.forEach(x => {
    arr2.forEach(y => {
      if (x.id === y.id) {
        temp.push({ ...x, ...y })
      }
    })
  })

  return temp
}

console.log(merge(a1, a2))

Solution 8 - Javascript

The lodash implementaiton:

var merged = _.map(a1, function(item) {
	return _.assign(item, _.find(a2, ['id', item.id]));
});

The result:

[     {        "id":1,      "name":"test",      "count":"1"   },   {        "id":2,      "name":"test2",      "count":"2"   }]

Solution 9 - Javascript

A working TypeScript version:

export default class Merge {
  static byKey(a1: any[], a2: any[], key: string) {
    const res = a1.concat(a2).reduce((acc, x) => {
      acc[x[key]] = Object.assign(acc[x[key]] || {}, x);
      return acc;
    }, {});

    return Object.entries(res).map(pair => {
      const [, value] = pair;
      return value;
    });
  }
}

test("Merge", async () => {
  const a1 = [{ id: "1", value: "1" }, { id: "2", value: "2" }];
  const a2 = [{ id: "2", value: "3" }];

  expect(Merge.byKey(a1, a2, "id")).toStrictEqual([
    {
      id: "1",
      value: "1"
    },
    { id: "2", value: "3" }
  ]);
});

Solution 10 - Javascript

const a3 = a1.map(it1 => {
   it1.test = a2.find(it2 => it2.id === it1.id).test
   return it1
 })

Solution 11 - Javascript

Wanted to add this answer which is derived from @daisihi answer above. Main difference is that this uses the spread operator. Also, at the end I remove the id because it was not desirable in the first place.

const a3 = [...a1, ...a2].reduce((acc, x) => {
   acc[x.id] = {...acc[x.id] || {}, ...x};
   return acc;
}, {});

This part was taken from another post. removing a property from a list of objects in an array

const newArray = Object.values(a3).map(({id, ...keepAttrs}) => keepAttrs);

Solution 12 - Javascript

try this

var a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
var a2 = [{ id : 1, count : "1"}, {id : 2, count : "2"}]
let arr3 = a1.map((item, i) => Object.assign({}, item, a2[i]));

console.log(arr3);

Solution 13 - Javascript

How about this?

const mergeArrayObjects = (arr1: any[], arr2: any[], mergeByKey: string): any[] => {
  const updatedArr = [];
  for (const obj of arr1) {
    const arr1ValueInArr2 = arr2.find(
      a => a[mergeByKey] === obj[mergeByKey],
    );
    if (arr1ValueInArr2) {
      updatedArr.push(Object.assign(obj, arr1ValueInArr2));
    } else {
      updatedArr.push(obj);
    }
  }
  const mergeByKeyValuesInArr1 = arr1.map(a => a[mergeByKey]);
  const remainingObjInArr2 = arr2.filter(a => !mergeByKeyValuesInArr1.includes(a[mergeByKey]) )
  return updatedArr.concat(remainingObjInArr2)
}

Solution 14 - Javascript

You can write a simple object merging function like this

function mergeObject(cake, icing) {
    var icedCake = {}, ingredient;
    for (ingredient in cake)
        icedCake[ingredient] = cake[ingredient];
    for (ingredient in icing)
        icedCake[ingredient] = icing[ingredient];
    return icedCake;
}

Next, you need to do use a double-loop to apply it to your data structre

var i, j, a3 = a1.slice();
for (i = 0; i < a2.length; ++i)                // for each item in a2
    for (j = 0; i < a3.length; ++i)            // look at items in other array
        if (a2[i]['id'] === a3[j]['id'])       // if matching id
            a3[j] = mergeObject(a3[j], a2[i]); // merge

You can also use mergeObject as a simple clone, too, by passing one parameter as an empty object.

Solution 15 - Javascript

If you have exactly the same number of items in both array with same ids you could do something like this.

const mergedArr = arr1.map((item, i) => {
  if (item.ID === arr2[i].ID) {
	return Object.assign({}, item, arr2[i]);
  }
});

Solution 16 - Javascript

function mergeDiffs(Schedulearray1, Schedulearray2) {
    var secondArrayIDs = Schedulearray2.map(x=> x.scheduleid);
    return Schedulearray1.filter(x=> !secondArrayIDs.includes(x.scheduleid)).concat(Schedulearray2);   
}

Solution 17 - Javascript

function arrayUnique(array) {
    var a = array.concat();
    for (var i = 0; i < a.length; ++i) {
        for (var j = i + 1; j < a.length; ++j) {
            if (a[i] === a[j])
            a.splice(j--, 1);
        }
    }
    return a;
}

Solution 18 - Javascript

Found other solutions failing for some cases, so writing a better one here

const a1 = [{ id : 1, name : "test"}, { id : 2, name : "test2"}]
const a2 = [{ id : 3, count : "3"}, { id : 1, count : "1"}, {id : 2, count : "2"}]


const mergeHelper = new Map(a1.map(x => [x.id, x]));

for (const x of a2) {
    if (mergeHelper.has(x.id)) {
        const item = mergeHelper.get(x.id);
        mergeHelper.set(x.id, {...item, ...x});
    } else {
        mergeHelper.set(x.id, x);
    }
}

const mergedList = [...mergeHelper.values()];
// For sorted array 
// const mergedSortedList = [...mergeHelper.values()].sort((a, b) => a.id - b.id);

console.log(mergedList)

Using js Map is way faster than other approaches, helps when array length is huge.

Solution 19 - Javascript

None of them worked for me. I wrote own:

const formatteddata=data.reduce((a1,a2)=>{

for (let t=0; t<a1.length; t++)
    {var id1=a1[t].id
            for (let tt=0; tt<a2.length; tt++)
                {var id2=a2[tt].id
                    if(id1==date2)
                      {a1[t]={...a1[t],...a2[tt]}}
                }
    }
return a1

})

works with any amount of arrays of objects in arrays, with varying length and not always coinsciding dates

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionTam2View Question on Stackoverflow
Solution 1 - JavascriptAndreas TzionisView Answer on Stackoverflow
Solution 2 - JavascriptGruff BunnyView Answer on Stackoverflow
Solution 3 - JavascriptOriolView Answer on Stackoverflow
Solution 4 - JavascriptAlbertoView Answer on Stackoverflow
Solution 5 - JavascriptDaishi NakajimaView Answer on Stackoverflow
Solution 6 - JavascriptNickView Answer on Stackoverflow
Solution 7 - JavascriptNick OlayView Answer on Stackoverflow
Solution 8 - Javascriptbora89View Answer on Stackoverflow
Solution 9 - JavascriptJeff TianView Answer on Stackoverflow
Solution 10 - Javascriptquy phamView Answer on Stackoverflow
Solution 11 - JavascriptChristian MatthewView Answer on Stackoverflow
Solution 12 - JavascriptTrilok SinghView Answer on Stackoverflow
Solution 13 - JavascriptmnihalkView Answer on Stackoverflow
Solution 14 - JavascriptPaul S.View Answer on Stackoverflow
Solution 15 - JavascriptCharitha GoonewardenaView Answer on Stackoverflow
Solution 16 - JavascriptAminFarajzadehView Answer on Stackoverflow
Solution 17 - JavascriptGauravView Answer on Stackoverflow
Solution 18 - JavascriptshijinView Answer on Stackoverflow
Solution 19 - Javascript3cirlcesView Answer on Stackoverflow