Search code examples
javascripttypescriptjavascript-objects

JavaScript merge arrays unshift with unique IDs


I've two arrays of objects:

var arr1 = [{
  "id": "id_1",
  "name": "Skoda"
}, {
  "id": "id_2",
  "name": "BMW"
}];

var arr2 = [{
  "id": "id_1",
  "name": "Skoda - Auto"
}, {
  "id": "id_3",
  "name": "BMW - Auto"
},{
  "id": "id_4",
  "name": "Merc - Auto"
}]

Output

var arr1 = [{
  "id": "id_3",
  "name": "BMW - Auto"
},{
  "id": "id_4",
  "name": "Merc - Auto"
},{
  "id": "id_1",
  "name": "Skoda"
}, {
  "id": "id_2",
  "name": "BMW"
}];

I want to merge two arrays arr1 & arr2, basically I want to unshift the object from arr2 to the arr1 only if the Id is not present in arr1, Hence we're not adding id_1 to the arr1. I'm not sure how can we achieve it

I tried the below way but it keeps on adding duplicate items, how can I avoid duplication of id

arr1.unshift.apply(arr1, arr2)


Solution

  • You can reduce all the datasets onto a Map.

    For each item in a dataset, prioritize the first occurrence by assigning it last.

    Note: accInner is technically accOuter, but they are distinguished as separate variables for the sake of the accumulator argument for each reduce operation. They are both references to the Map() initializer.

    Also, k is the value of the id field (key) of item[key]. I used an IIFE (Immediately Invoked Function Execution) to avoid creating a block-scope and assigning it as a variable.

    // First occurrence of `name` and `a` stays
    const arr1 = [
      { "id": "id_1", "name": "Skoda", "a": 1 },
      { "id": "id_2", "name": "BMW" }
    ];
    
    // Do not override `name` or `a`, but add `b`
    const arr2 = [
      { "id": "id_1", "name": "Skoda - Auto", "a": 0, "b": 2 },
      { "id": "id_3", "name": "BMW - Auto" },
      { "id": "id_4", "name": "Merc - Auto" }
    ];
    
    const mergeByKey = (key, ...dataSets) =>
      [...dataSets
        .reduce((accOuter, dataSet) =>
          dataSet.reduce((accInner, item) =>
            ((k) =>
              accInner.set(k, Object.assign({}, item, accInner.get(k)))) // priority
            (item[key]), accOuter), new Map)
        .values()]
    
    const arr3 = mergeByKey('id', arr1, arr2); // Priority is left-to-right
    
    console.log(arr3);
    .as-console-wrapper { top: 0; max-height: 100% !important; }