Search code examples
javascriptarraysjavascript-objects

Merge two n number of array of objects based on a key


I have two arrays:

Array 1:

[
    {old_clicks: 1, date: "2017-01-24" }, 
    { old_clicks: 4, date: "2017-01-22" },
    { old_clicks: 6, date: "2017-01-21" }
]

and array 2:

[
    { total_clicks:120, date: "2017-01-21" },
    { total_clicks: 100, date: "2017-01-19" }
]

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

[
    { old_clicks: 1,date: "2017-01-24", total_clicks: 0 },
    { old_clicks: 4, date: "2017-01-22", total_clicks: 0 },
    { old_clicks: 6, date: "2017-01-21", total_clicks: 120 },
    { old_clicks: 0, date: "2017-01-19", total_clicks: 100 },
]

I have tried this but this is not working as its not merging properly.

const arr1 = [
    { old_clicks: 1, date: "2017-01-24" }, 
    { old_clicks: 4, date: "2017-01-22" },
    { old_clicks: 6, date: "2017-01-21" }
];


const arr2 =  [
    { total_clicks:120, date: "2017-01-21" },
    { total_clicks: 100, date: "2017-01-19" }
];
        

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

console.log(mergeById(arr1,arr2))


Solution

  • This looks like a case for Array.reduce.

    The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in single output value.

    The signature is:

    arr.reduce(callback( accumulator, currentValue, [, index[, array]] )[, initialValue])
    

    The approach here is to add items using their date property as a key to a "map" object that starts out initialized as an empty object, then summing in the clicks properties you care about. At the end, result will be an object and we can use Object.values() to turn it's values into an array. We choose to use an object internally here to for faster lookup of existing dates, you could reduce using an array instead, but naively using arr.find would net you a O(n2) solution.

    const arr1 = [
      { old_clicks: 1, date: "2017-01-24" },
      { old_clicks: 4, date: "2017-01-22" },
      { old_clicks: 6, date: "2017-01-21" }
    ];
    
    
    const arr2 = [
      { total_clicks: 120, date: "2017-01-21" },
      { total_clicks: 100, date: "2017-01-19" }
    ];
    
    
    const mergeById = (...arrs) => {
    
      const result = arrs.flat().reduce((acc, obj) => {
        acc[obj.date] = {
          date: obj.date,
          total_clicks: (acc[obj.date]?.total_clicks ?? 0) + (obj?.total_clicks ?? 0),
          old_clicks: (acc[obj.date]?.old_clicks ?? 0) + (obj?.old_clicks ?? 0),
        }
    
        return acc;
      }, {})
      
      return Object.values(result);
    }
    
    console.log(mergeById(arr1, arr2))