Merge two array of objects based on a key

Javascript

Javascript Problem Overview


I have two arrays:

Array 1:

[
  { id: "abdc4051", date: "2017-01-24" }, 
  { id: "abdc4052", date: "2017-01-22" }
]

and array 2:

[
  { id: "abdc4051", name: "ab" },
  { id: "abdc4052", name: "abc" }
]

I need to merge these two arrays based on id and get this:

[
  { id: "abdc4051", date: "2017-01-24", name: "ab" },
  { id: "abdc4052", date: "2017-01-22", name: "abc" }
]

How can I do this without iterating trough Object.keys?

Javascript Solutions


Solution 1 - Javascript

You can do it like this -

let arr1 = [
    { id: "abdc4051", date: "2017-01-24" },
    { id: "abdc4052", date: "2017-01-22" }
];

let arr2 = [
    { id: "abdc4051", name: "ab" },
    { id: "abdc4052", name: "abc" }
];

let arr3 = arr1.map((item, i) => Object.assign({}, item, arr2[i]));

console.log(arr3);


Use below code if arr1 and arr2 are in a different order:

let arr1 = [
  { id: "abdc4051", date: "2017-01-24" }, 
  { id: "abdc4052", date: "2017-01-22" }
];

let arr2 = [
  { id: "abdc4051", name: "ab" },
  { id: "abdc4052", name: "abc" }
];

let merged = [];

for(let i=0; i<arr1.length; i++) {
  merged.push({
   ...arr1[i], 
   ...(arr2.find((itmInner) => itmInner.id === arr1[i].id))}
  );
}

console.log(merged);

Use this if arr1 and arr2 are in a same order

let arr1 = [
  { id: "abdc4051", date: "2017-01-24" }, 
  { id: "abdc4052", date: "2017-01-22" }
];

let arr2 = [
  { id: "abdc4051", name: "ab" },
  { id: "abdc4052", name: "abc" }
];

let merged = [];

for(let i=0; i<arr1.length; i++) {
  merged.push({
   ...arr1[i], 
   ...arr2[i]
  });
}

console.log(merged);

Solution 2 - Javascript

You can do this in one line

let arr1 = [
    { id: "abdc4051", date: "2017-01-24" },
    { id: "abdc4052", date: "2017-01-22" }
];

let arr2 = [
    { id: "abdc4051", name: "ab" },
    { id: "abdc4052", name: "abc" }
];

const mergeById = (a1, a2) =>
    a1.map(itm => ({
        ...a2.find((item) => (item.id === itm.id) && item),
        ...itm
    }));

console.log(mergeById(arr1, arr2));

  1. Map over array1
  2. Search through array2 for array1.id
  3. If you find it ...spread the result of array2 into array1

The final array will only contain id's that match from both arrays

Solution 3 - Javascript

This solution is applicable even when the merged arrays have different sizes. Also, even if the matching keys have different names.

Merge the two arrays by using a Map as follows:

const arr1 = [ { id: "abdc4051", date: "2017-01-24" }, { id: "abdc4052", date: "2017-01-22" }, { id: "abdc4053", date: "2017-01-22" } ]; const arr2 = [ { nameId: "abdc4051", name: "ab" }, { nameId: "abdc4052", name: "abc" } ];

const map = new Map();
arr1.forEach(item => map.set(item.id, item));
arr2.forEach(item => map.set(item.nameId, {...map.get(item.nameId), ...item}));
const mergedArr = Array.from(map.values());

console.log(JSON.stringify(mergedArr));

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

Run the stack snippet to see the result:

[  {    "id": "abdc4051",    "date": "2017-01-24",    "nameId": "abdc4051",    "name": "ab"  },  {    "id": "abdc4052",    "date": "2017-01-22",    "nameId": "abdc4052",    "name": "abc"  },  {    "id": "abdc4053",    "date": "2017-01-22"  }]

Solution 4 - Javascript

You could use an arbitrary count of arrays and map on the same index new objects.

var array1 = [{ id: "abdc4051", date: "2017-01-24" }, { id: "abdc4052", date: "2017-01-22" }], array2 = [{ id: "abdc4051", name: "ab" }, { id: "abdc4052", name: "abc" }], result = [array1, array2].reduce((a, b) => a.map((c, i) => Object.assign({}, c, b[i])));

console.log(result);

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

Solution 5 - Javascript

Here's an O(n) solution using reduce and Object.assign

const joinById = ( ...lists ) =>
    Object.values(
        lists.reduce(
            ( idx, list ) => {
                list.forEach( ( record ) => {
                    if( idx[ record.id ] )
                        idx[ record.id ] = Object.assign( idx[ record.id ], record)
                    else
                        idx[ record.id ] = record
                } )
                return idx
            },
            {}
        )
    )

To use this function for the OP's case, pass in the arrays you want to join to joinById (notice lists is a rest parameter).

let joined = joinById(list1, list2)

Each list gets reduced to a single object where the keys are ids and the values are the objects. If there's a value at the given key already, it gets object.assign called on it and the current record.

Here's the generic O(n*m) solution, where n is the number of records and m is the number of keys. This will only work for valid object keys. You can convert any value to base64 and use that if you need to.

const join = ( keys, ...lists ) =>
    lists.reduce(
        ( res, list ) => {
            list.forEach( ( record ) => {
                let hasNode = keys.reduce(
                    ( idx, key ) => idx && idx[ record[ key ] ],
                    res[ 0 ].tree
                )
                if( hasNode ) {
                    const i = hasNode.i
                    Object.assign( res[ i ].value, record )
                    res[ i ].found++
                } else {
                    let node = keys.reduce( ( idx, key ) => {
                        if( idx[ record[ key ] ] )
                            return idx[ record[ key ] ]
                        else
                            idx[ record[ key ] ] = {}
                        return idx[ record[ key ] ]
                    }, res[ 0 ].tree )
                    node.i = res[ 0 ].i++
                    res[ node.i ] = {
                        found: 1,
                        value: record
                    }
                }
            } )
            return res
        },
        [ { i: 1, tree: {} } ]
         )
         .slice( 1 )
         .filter( node => node.found === lists.length )
         .map( n => n.value )

This is essentially the same as the joinById method, except that it keeps an index object to identify records to join. The records are stored in an array and the index stores the position of the record for the given key set and the number of lists it's been found in.

Each time the same key set is encountered, it finds the node in the tree, updates the element at it's index, and the number of times it's been found is incremented.

After joining, the idx object is removed from the array with the slice and any elements that weren't found in each set are removed. This makes it an inner join, you could remove this filter and have a full outer join.

Finally each element is mapped to it's value, and you have the joined arrays.

Solution 6 - Javascript

To merge the two arrays on id, assuming the arrays are equal length:

arr1.map(item => ({
    ...item,
    ...arr2.find(({ id }) => id === item.id),
}));

Solution 7 - Javascript

If you have 2 arrays need to be merged based on values even its in different order

let arr1 = [
    { id:"1", value:"this", other: "that" },
    { id:"2", value:"this", other: "that" }
];

let arr2 = [
    { id:"2", key:"val2"},
    { id:"1", key:"val1"}
];

you can do like this

const result = arr1.map(item => {
    const obj = arr2.find(o => o.id === item.id);
    return { ...item, ...obj };
  });

console.log(result);

Solution 8 - Javascript

We can use lodash here. _.merge works as you expected. It works with the common key present.

_.merge(array1, array2)

Solution 9 - Javascript

You can use array methods

let arrayA=[
{id: "abdc4051", date: "2017-01-24"},
{id: "abdc4052", date: "2017-01-22"}]

let arrayB=[
{id: "abdc4051", name: "ab"},
{id: "abdc4052", name: "abc"}]

let arrayC = [];




arrayA.forEach(function(element){
  arrayC.push({
  id:element.id,
  date:element.date,
  name:(arrayB.find(e=>e.id===element.id)).name
  });  
});

console.log(arrayC);

//0:{id: "abdc4051", date: "2017-01-24", name: "ab"}
//1:{id: "abdc4052", date: "2017-01-22", name: "abc"}

Solution 10 - Javascript

You can recursively merge them into one as follows:

function mergeRecursive(obj1, obj2) {
    for (var p in obj2) {
        try {
            // Property in destination object set; update its value.
            if (obj2[p].constructor == Object) {
                obj1[p] = this.mergeRecursive(obj1[p], obj2[p]);

            } else {
                obj1[p] = obj2[p];

            }

        } catch (e) {
            obj1[p] = obj2[p];

        }
    }
    return obj1;
}

arr1 = [
    { id: "abdc4051", date: "2017-01-24" },
    { id: "abdc4052", date: "2017-01-22" }
];
arr2 = [
    { id: "abdc4051", name: "ab" },
    { id: "abdc4052", name: "abc" }
];

mergeRecursive(arr1, arr2)
console.log(JSON.stringify(arr1))

Solution 11 - Javascript

Non of these solutions worked for my case:

  • missing objects can exist in either array
  • runtime complexity of O(n)

notes:

  • I used lodash but it's easy to replace with something else
  • Also used Typescript (just remove/ignore the types)
import { keyBy, values } from 'lodash';

interface IStringTMap<T> {
  [key: string]: T;
}

type IIdentified = {
  id?: string | number;
};

export function mergeArrayById<T extends IIdentified>(
  array1: T[],
  array2: T[]
): T[] {
  const mergedObjectMap: IStringTMap<T> = keyBy(array1, 'id');

  const finalArray: T[] = [];

  for (const object of array2) {
    if (object.id && mergedObjectMap[object.id]) {
      mergedObjectMap[object.id] = {
        ...mergedObjectMap[object.id],
        ...object,
      };
    } else {
      finalArray.push(object);
    }
  }

  values(mergedObjectMap).forEach(object => {
    finalArray.push(object);
  });

  return finalArray;
}

Solution 12 - Javascript

Irrespective of the order you can merge it by,

function merge(array,key){
    let map = {};
    array.forEach(val=>{
        if(map[val[key]]){
            map[val[key]] = {...map[val[key]],...val};
        }else{
            map[val[key]] = val;
        }
    })
    return Object.keys(map).map(val=>map[val]);
}

let b = [
  { id: "abdc4051", name: "ab" },
  { id: "abdc4052", name: "abc" }
];
let a = [
  { id: "abdc4051", date: "2017-01-24" }, 
  { id: "abdc4052", date: "2017-01-22" }
];

console.log(merge( [...a,...b], 'id'));

Solution 13 - Javascript

Here is one-liner (order of elements in array is not important and assuming there is 1 to 1 relationship):

var newArray = array1.map(x=>Object.assign(x, array2.find(y=>y.id==x.id)))

Solution 14 - Javascript

I iterated through the first array and used the .find method on the second array to find a match where the id are equal and returned the result.

const a = [{ id: "abdc4051", date: "2017-01-24" },{ id: "abdc4052", date: "2017-01-22" }];
const b = [{ id: "abdc4051", name: "ab" },{ id: "abdc4052", name: "abc" }];

console.log(a.map(itm => ({...itm, ...b.find(elm => elm.id == itm.id)})));

Solution 15 - Javascript

Well... assuming both arrays are of the same length, I would probably do something like this:

var newArr = []
for (var i = 0; i < array1.length; i++ {
    if (array1[i].id === array2[i].id) {
      newArr.push({id: array1[i].id, date: array1[i].date, name: array2[i].name});
  }
}

Solution 16 - Javascript

I was able to achieve this with a nested mapping of the two arrays and updating the initial array:

member.map(mem => {
return memberInfo.map(info => {
    if (info.id === mem.userId) {
        mem.date = info.date;
        return mem;
        }
    }
}

Solution 17 - Javascript

An approach if both two arrays have non-intersect items.

const firstArray = [
  { id: 1, name: "Alex", salutation: "Mr." },
  { id: 2, name: "Maria", salutation: "Ms." },
];

const secondArray = [
  { id: 2, address: "Larch Retreat 31", postcode: "123452" },
  { id: 3, address: "Lycroft Close 12D", postcode: "123009" },
];

const mergeArr = (arr1, arr2) => {
  const obj = {};

  arr1.forEach(item => {
    obj[item.id] = item;
  });

  arr2.forEach(item => {
    obj[item.id]
      ? (obj[item.id] = { ...obj[item.id], ...item })
      : (obj[item.id] = item);
  });

  return Object.values(obj);
};

const output = mergeArr(firstArray, secondArray);

console.log(output);

Solution 18 - Javascript

There are a lot of solutions available for this, But, We can simply use for loop and if conditions to get merged arrays.

const firstArray = [
  { id: 1, name: "Alex", salutation: "Mr." },
  { id: 2, name: "Maria", salutation: "Ms." },
];

const secondArray = [
  { id: 1, address: "Larch Retreat 31", postcode: "123452" },
  { id: 2, address: "Lycroft Close 12D", postcode: "123009" },
];

let mergedArray: any = [];

for (const arr1 of firstArray) {
  for (arr2 doc of secondArray) {
    if (arr1.id === arr2.id) {
      mergedArray.push({ ...arr1, ...arr2 });
    }
  }
}

console.log(mergedArray)

Solution 19 - Javascript

This is a version when you have an object and an array and you want to merge them and give the array a key value so it fits into the object nicely.

var fileData = [
    { "id" : "1", "filename" : "myfile1", "score" : 33.1 }, 
    { "id" : "2", "filename" : "myfile2", "score" : 31.4 }, 
    { "id" : "3", "filename" : "myfile3", "score" : 36.3 }, 
    { "id" : "4", "filename" : "myfile4", "score" : 23.9 }
];

var fileQuality = [0.23456543,0.13413131,0.1941344,0.7854522];

var newOjbect = fileData.map((item, i) => Object.assign({}, item, {fileQuality:fileQuality[i]}));

console.log(newOjbect);

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
QuestionAdelView Question on Stackoverflow
Solution 1 - JavascriptRajaprabhu AravindasamyView Answer on Stackoverflow
Solution 2 - JavascriptTrevor JosephView Answer on Stackoverflow
Solution 3 - JavascriptjsbishtView Answer on Stackoverflow
Solution 4 - JavascriptNina ScholzView Answer on Stackoverflow
Solution 5 - JavascriptSnowbuilderView Answer on Stackoverflow
Solution 6 - JavascriptKris BurkeView Answer on Stackoverflow
Solution 7 - JavascriptNIsham MahsinView Answer on Stackoverflow
Solution 8 - JavascriptOmkarView Answer on Stackoverflow
Solution 9 - JavascriptRenzo CallaView Answer on Stackoverflow
Solution 10 - JavascriptloretoparisiView Answer on Stackoverflow
Solution 11 - JavascriptIanEdingtonView Answer on Stackoverflow
Solution 12 - Javascriptdeepak thomasView Answer on Stackoverflow
Solution 13 - JavascriptTomba_HRView Answer on Stackoverflow
Solution 14 - Javascripta.molaView Answer on Stackoverflow
Solution 15 - JavascriptQamar StationwalaView Answer on Stackoverflow
Solution 16 - JavascriptThadeus AjayiView Answer on Stackoverflow
Solution 17 - JavascriptikhvjsView Answer on Stackoverflow
Solution 18 - JavascriptDanielprabhakaran NView Answer on Stackoverflow
Solution 19 - JavascriptDarragh BlakeView Answer on Stackoverflow