Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to merge objects with the same properties into an Array?

I would like to merge 2 objects with the same properties into an Array.

Take this for an example:

object1 = {"id":1,
           "name":name1,
           "children":[{"id":2,"name":name2}]
          };
object2 = {"id":3,
           "name":name3,
           "children":[{"id":4,"name":name4}]
          };
object3 = {"id":1,
           "name":name1,
           "children":[{"id":6,"name":name6}]
          };
var result = Object.assign(result,object1,object2,object3);

Expected result:

JSON.stringify([result]) =[
                           {"id":1,
                            "name":name1,
                            "children":[{"id":2,"name":name2},
                                       {"id":6,"name":name6}]
                           },
                           {"id":3,
                            "name":name3,
                            "children":[{"id":4,"name":name4}]
                           }
                          ]

Actual result:

JSON.stringify([result]) = [
                            {"id":3,
                             "name":name3,
                             "children":[{"id":4,"name":name4}]
                            }
                           ]

Seems like Object.assign() isn't the way to go... as it will overwrite, I do not want it to overwrite, I want them to merge instead. Is there a right way to do this?

like image 368
Ronaldo Avatar asked Dec 11 '22 09:12

Ronaldo


1 Answers

As so often, Array.prototype.reduce provides a good base for an approach like e.g. this one ...

var obj1 = {
  "id": 1,
  "name": "name1",
  "children": [{ "id": 2, "name": "name2" }]
};
var obj2 = {
  "id": 3,
  "name": "name3",
  "children": [{ "id": 4, "name": "name4" }]
};
var obj3 = {
  "id": 1,
  "name": "name1",
  "children": [{ "id": 6, "name": "name6" }]
};
// Expected result: [{
//   "id": 1,
//   "name": name1,
//   "children": [
//     { "id": 2, "name": "name2" },
//     { "id": 6, "name": "name6" }
//   ]
// }, {
//   "id": 3,
//   "name": "name3",
//   "children": [{"id": 4, "name": "name4" }]
// }]

function mergeEquallyLabeledTypes(collector, type) {
  var key = (type.name + '@' + type.id); // identity key.
  var store = collector.store;
  var storedType = store[key];
  if (storedType) { // merge `children` of identically named types.
    storedType.children = storedType.children.concat(type.children);
  } else {
    store[key] = type;
    collector.list.push(type);
  }
  return collector;
}

var result = [obj1, obj2, obj3].reduce(mergeEquallyLabeledTypes, {

  store:  {},
  list:   []

}).list;

console.log('result : ', result);
.as-console-wrapper { max-height: 100%!important; top: 0; }

Edit Note

After having been informed about changed requirements, that need to deal with a nested pattern, I will change my first provided approach into a generic solution. It will be not that difficult since there is a generically repeated pattern within the data structure. Thus I just need to make the already existing reducer function self recursive. A recursion step will be triggered after having finished a complete reducing cycle on any provided list ...

var obj1 = {
  "id": 1,
  "name": "name1",
  "children": [{ "id": 2, "name": "name2", "children": [{ "id": 8, "name": "name8" }] }]
};
var obj2 = {
  "id": 3,
  "name": "name3",
  "children": [{ "id": 4, "name": "name4", "children": [{ "id": 9, "name": "name9" }] }]
};
var obj3 = {
  "id": 1,
  "name": "name1",
  "children": [{ "id": 6, "name": "name6", "children": [{ "id": 10, "name": "name10" }] }]
};
var obj4 = {
  "id": 3,
  "name": "name3",
  "children": [{ "id": 4, "name": "name4", "children": [{ "id": 11, "name": "name11" }] }]
};

function mergeEquallyLabeledTypesRecursively(collector, type, idx, list) {
  var key = (type.name + '@' + type.id); // identity key.
  var store = collector.store;
  var storedType = store[key];
  if (storedType) { // merge `children` of identically named types.
    storedType.children = storedType.children.concat(type.children);
  } else {
    store[key] = type;
    collector.list.push(type);
  }
  // take repetitive data patterns into account ...
  if (idx >= (list.length - 1)) {
    collector.list.forEach(function (type) {

      // ... behave recursive, when appropriate.
      if (type.children) {
        type.children = type.children.reduce(mergeEquallyLabeledTypesRecursively, {

          store:  {},
          list:   []

        }).list;
      }
    });
  }
  return collector;
}

var result = [obj1, obj2, obj3, obj4].reduce(mergeEquallyLabeledTypesRecursively, {

  store:  {},
  list:   []

}).list;

console.log('result : ', result);
.as-console-wrapper { max-height: 100%!important; top: 0; }
like image 176
Peter Seliger Avatar answered Feb 22 '23 14:02

Peter Seliger