0

I have to merge 2 arrays with key value as follows:

array1 = [
{id:"123", data:[{id:"234",data:"hello"},{id:"345",data:"there"},{id:"xyz", data:"yo"}]},
{id:"456", data:[{id:"34",data:"test"},{id:"45",data:"test2"},{id:"yz", data:"test3"}]},
{id:"789", data:[{id:"23",data:"aaa"},{id:"34",data:"bbb"},{id:"xy", data:"ccc"}]}]

with

array2 = [
{id:"456", data:[{id:"45",data:"changed"},{id:"yz", data:"data"}]},
{id:"789", data:[{id:"456",data:"appended data"}]},
{id:"890", data:[{id:"456",data:"new data"}]}]

to produce something like

merged = [
{id:"123", data:[{id:"234",data:"hello"},{id:"345",data:"there"},{id:"xyz", data:"yo"}]},
{id:"456", data:[{id:"34",data:"test"},{id:"45",data:"changed"},{id:"yz", data:"data"}]},
{id:"789", data:[{id:"23",data:"aaa"},{id:"34",data:"bbb"},{id:"xy", data:"ccc"},{id:"456",data:"appended data"}]},
{id:"890", data:[{id:"456",data:"new data"}]}]

I've been trying this out for quite some time and can't get a solution that meets the scenario. Most of the solutions just do blind merging, not based on the id value. Tried using lodash mergeWith but didn't get the output needed. A Ramda solution is also acceptable.

Thanks,

Gavin Ang
  • 125
  • 2
  • 8

3 Answers3

0

This links could be helpful to you merge two arrays.

In this code snippet, i have tried to find the common objects between set1 and set2,if there are any i'm finding the unique properties and changing their content and also non existant properties in object2 and pushing it to object1

Check the following snippet.

var arr1 = [{
  id: "123",
  data: [{
    id: "234",
    data: "hello"
  }, {
    id: "345",
    data: "there"
  }, {
    id: "xyz",
    data: "yo"
  }]
}, {
  id: "456",
  data: [{
    id: "34",
    data: "test"
  }, {
    id: "45",
    data: "test2"
  }, {
    id: "yz",
    data: "test3"
  }]
}, {
  id: "789",
  data: [{
    id: "23",
    data: "aaa"
  }, {
    id: "34",
    data: "bbb"
  }, {
    id: "xy",
    data: "ccc"
  }]
}]

var arr2 = [{
  id: "456",
  data: [{
    id: "45",
    data: "changed"
  }, {
    id: "yz",
    data: "data"
  }]
}, {
  id: "789",
  data: [{
    id: "456",
    data: "appended data"
  }]
}, {
  id: "890",
  data: [{
    id: "456",
    data: "new data"
  }]
}]
var arr3 = [];
for (var i in arr1) {
  var shared = false;
  for (var j in arr2)
    if (arr2[j].id == arr1[i].id) {
      shared = true;
      //  arr1[i].data.concat(arr2[j].data);
      var set1 = pushproperties(arr1[i].data, arr2[j].data);
      arr1[i].data = set1;
      arr3.push(arr1[i]);
      break;
    }
  if (!shared) {
    arr3.push(arr1[i]);
    arr3.push(arr2[j]);
  }

}

function pushproperties(set1, set2) {
  var filtered = false;
  set2.forEach(function(item) {
    filtered = set1.every(function(element) {
      return element.id != item.id;

    });
    if (filtered) {
      set1.push(item);
    }

  });

  set1.forEach(function(item) {
    set2.forEach(function(element) {
      if (item.id == element.id) {
        item.data = element.data;
      }

    });


  });

  return set1;

}

console.log(arr3);

Hope this helps

Community
  • 1
  • 1
Geeky
  • 7,420
  • 2
  • 24
  • 50
  • Thanks for this. This seems closer to what I need, and it almost.... works. A couple of problems: – Gavin Ang Nov 29 '16 at 10:27
  • Thanks for this. This seems closer to what I need, and it almost.... works. A couple of problems: 1. If array 2 has only 1 item that matches to the 2nd item of array 1, then I will get both the array 2 object and the merged object pushed into arr 3.
    I commented out the arr3.push(arr2[j]) to resolve this problem, but if arr 2 has nothing that matches arr1, the arr2 object doesn't get pushed into arr3
    – Gavin Ang Nov 29 '16 at 10:32
0

This a function the merges 2 arrays recursively using Array.prototype.reduce(). If it encounters items with the same id, and they have a data prop, which is an array, it merges them using the logic. If data is not an array, it's overridden by the last item instead.

function mergeArraysDeep(arr1, arr2) { 
  var unique = arr1.concat(arr2).reduce(function(hash, item) {
    var current = hash[item.id];
    
    if(!current) {
      hash[item.id] = item;
    } else if (Array.isArray(current.data)) {
      current.data = mergeArraysDeep(current.data, item.data);
    } else {
      current.data = item.data;
    }

    return hash;
  }, {});

  return Object.keys(unique).map(function(key) {
    return unique[key];
  });
}

var array1 = [
  {id:"123", data:[{id:"234",data:"hello"},{id:"345",data:"there"},{id:"xyz", data:"yo"}]},
  {id:"456", data:[{id:"34",data:"test"},{id:"45",data:"test2"},{id:"yz", data:"test3"}]},
  {id:"789", data:[{id:"23",data:"aaa"},{id:"34",data:"bbb"},{id:"xy", data:"ccc"}]}
];

var array2 = [
  {id:"456", data:[{id:"45",data:"changed"},{id:"yz", data:"data"}]},
  {id:"789", data:[{id:"456",data:"appended data"}]},
  {id:"890", data:[{id:"456",data:"new data"}]}
];

var result = mergeArraysDeep(array1, array2)

console.log(result);

ES6 version that uses Map, Map.prototype.values(), and array spread:

const mergeArraysDeep = (arr1, arr2) => { 
  return [...arr1.concat(arr2).reduce((hash, item) => {
    const current = hash.get(item.id);
    
    if(!current) {
      hash.set(item.id, item);
    } else if (Array.isArray(current.data)) {
      current.data = mergeArraysDeep(current.data, item.data);
    } else {
      current.data = item.data;
    }

    return hash;
  }, new Map()).values()];
}

const array1 = [
  {id:"123", data:[{id:"234",data:"hello"},{id:"345",data:"there"},{id:"xyz", data:"yo"}]},
  {id:"456", data:[{id:"34",data:"test"},{id:"45",data:"test2"},{id:"yz", data:"test3"}]},
  {id:"789", data:[{id:"23",data:"aaa"},{id:"34",data:"bbb"},{id:"xy", data:"ccc"}]}
];

const array2 = [
  {id:"456", data:[{id:"45",data:"changed"},{id:"yz", data:"data"}]},
  {id:"789", data:[{id:"456",data:"appended data"}]},
  {id:"890", data:[{id:"456",data:"new data"}]}
];

const result = mergeArraysDeep(array1, array2)

console.log(result);
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
  • Thanks for this. I tried this on various combinations of data, but couldn't get the inner array to be merged properly. – Gavin Ang Nov 29 '16 at 10:25
  • Yes I have. My real world data has different combos, and while I thought your solution would work, it didn't merge the inner array like it did on the test data – Gavin Ang Nov 29 '16 at 10:46
0

Finally this is what worked for me. Thanks to @Geeky for showing the way:

function mergeArrays(arr1, arr2) {
  var arr3, arrIdx = [];
  if (!arr1 || arr1.length ==0) return arr2
  for (var i in arr1) {
    var shared = false;
    for (var j in arr2)
      if (arr2[j].id == arr1[i].id) {
        shared = true;
        joined = _.mergeWith({},arr1[i],arr2[j], function (a,b) {
          if (_.isArray(a)) return b.concat(a)})
        arr3.push(joined);
        break;
      }
    if (!shared) {
      arr3.push(arr1[i]);
    }
  }
  for (var k in arr2) {
    if (arrIdx[k] !=k) arr3.push(arr2[k])
  }
  return arr3

}
Gavin Ang
  • 125
  • 2
  • 8