3

I want to merge two arrays of objects. The keys are the same but the values might not always be the same.

Any solution is appreciated preferably in javascript, but a python solution is fine as well.

Here is the sample data:

var g= [ 
    { id: 36, name: 'AAA', goal: 'yes' , 'random':27},
    { id: 40, name: 'BBB', goal: 'yes' },
    { id: 39, name: 'JJJ', goal: 'yes' },
    { id: 27, name: 'CCC', goal: 'yes' , lag: "23.3343"}];


var c= [ 
    { id: 36, name: 'AAA', goal: 'yes', color:"purple" },
    { id: 40, name: 'BBB', circle: 'yes', color:"purple" },
    { id: 100, name: 'JJJ', circle: 'yes'} ];

My expected output should be :

 var finalData = [{
 { id: 36, name: 'AAA', goal: 'yes' ,'random':27, color:"purple"},
 { id: 40, name: 'BBB', circle: 'yes', color:"purple"},
 { id: 39, name: 'JJJ', goal: 'yes' },
 { id: 27, name: 'CCC', goal: 'yes' ,lag: "23.3343"},
 { id: 100, name: 'JJJ', circle: 'yes' }

  }]

Here is my current code, it works to some degree but it doesn't add keys it might have missed.

var finalData = [];
for(var i in g){
   var shared = false;
   for (var j in c)
       if (c[j].name == g[i].name) {
           shared = true;
           break;
       }
   if(!shared) finalData.push(g[i])
}
finalData = finalData.concat(c); 

finalData
str
  • 42,689
  • 17
  • 109
  • 127
Sayran
  • 163
  • 1
  • 2
  • 11

3 Answers3

4

You could use a Map for keeping same id in the same object and Object.assign for creating independent objects.

var g = [{ id: 36, name: 'AAA', goal: 'yes', 'random': 27 }, { id: 40, name: 'BBB', goal: 'yes' }, { id: 39, name: 'JJJ', goal: 'yes' }, { id: 27, name: 'CCC', goal: 'yes', lag: "23.3343" }],
    c = [{ id: 36, name: 'AAA', goal: 'yes', color: "purple" }, { id: 40, name: 'BBB', circle: 'yes', color: "purple" }, { id: 100, name: 'JJJ', circle: 'yes' }],
    map = new Map,
    result = g.concat(c).reduce(function (r, o) {
        var temp;
        if (map.has(o.id)) {
            Object.assign(map.get(o.id), o);
        } else {
            temp = Object.assign({}, o);
            map.set(temp.id, temp);
            r.push(temp);
        }
        return r;
    }, []);

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

Version without reduce and without concat.

function merge(o) {
    var temp;
    if (map.has(o.id)) {
        Object.assign(map.get(o.id), o);
        return;
    }
    temp = Object.assign({}, o);
    map.set(temp.id, temp);
    result.push(temp);
}

var g = [{ id: 36, name: 'AAA', goal: 'yes', 'random': 27 }, { id: 40, name: 'BBB', goal: 'yes' }, { id: 39, name: 'JJJ', goal: 'yes' }, { id: 27, name: 'CCC', goal: 'yes', lag: "23.3343" }],
    c = [{ id: 36, name: 'AAA', goal: 'yes', color: "purple" }, { id: 40, name: 'BBB', circle: 'yes', color: "purple" }, { id: 100, name: 'JJJ', circle: 'yes' }],
    map = new Map,
    result = [];

[g, c].forEach(function (a) {
    a.forEach(merge);
});

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

With dynamic key.

function mergeBy(key, data) {
    return Array.from(data
        .reduce((m, o) => m.set(o[key], { ...m.get(o[key]), ...o }), new Map)
        .values()
    );
}

var g = [{ id: 36, name: 'AAA', goal: 'yes', 'random': 27 }, { id: 40, name: 'BBB', goal: 'yes' }, { id: 39, name: 'JJJ', goal: 'yes' }, { id: 27, name: 'CCC', goal: 'yes', lag: "23.3343" }],
    c = [{ id: 36, name: 'AAA', goal: 'yes', color: "purple" }, { id: 40, name: 'BBB', circle: 'yes', color: "purple" }, { id: 100, name: 'JJJ', circle: 'yes' }],
    result = mergeBy('id', [...g, ...c]);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Can you explain what r.push(temp) does because r is not an array. Also when I tried this solution with a bigger data set it only returns only the last object instead of all of the objects it looped through. I am thinking its because reduce only returns a single value. I switched reduce with a map and it didn't work either . – Sayran Dec 09 '17 at 17:56
  • `r` is the accumulartor from reduce callback. it has as initial value an empty array. – Nina Scholz Dec 09 '17 at 17:58
  • when I tried this solution with a bigger data set it only returns only the last object instead of all of the objects it looped through. I am thinking its because reduce only returns a single value. I switched reduce with a map and it didn't work either – Sayran Dec 09 '17 at 18:14
  • it is always returning all objects which are consolidated. version 2 works without reduce now. – Nina Scholz Dec 09 '17 at 18:26
  • @NinaScholz Will the above solution (with reduce) works for large data sets? – Vishnu Aug 28 '20 at 03:08
  • @Vishnu, you could try it. i thing the second one performs better. – Nina Scholz Aug 28 '20 at 06:21
  • @NinaScholz sure i will try. Could you please help me to check this query? https://stackoverflow.com/questions/63628199/update-the-array-with-attributes-from-different-array-with-same-key-for-both-j – Vishnu Aug 28 '20 at 06:22
  • If I want to give the 'id' column dynamically and pass along with this function what changes do we need to make? – Toros91 Apr 07 '21 at 09:22
  • @Toros91, you could take a closure over the key and store the retuned function and use it like `merge` but with `o[key]` instead of `o.id`. – Nina Scholz Apr 07 '21 at 09:32
  • `[g, c].forEach(function (a) { a.forEach(merge);` in this how do we call? – Toros91 Apr 07 '21 at 09:33
  • 1
    please see third approach. – Nina Scholz Apr 07 '21 at 09:42
  • sorry for the trouble, one last question if we want to left join instead of inner join? – Toros91 Apr 07 '21 at 09:56
  • @Toros91, you could try yourself and ask a new question, because an answer would not fit above and not in commets. – Nina Scholz Apr 07 '21 at 10:26
  • @NinaScholz: posted the question here, https://stackoverflow.com/questions/66985164/two-arrays-left-join-in-javascript – Toros91 Apr 07 '21 at 11:28
1

Here's a Python solution. This modifies g, which you may or may not want.

c_by_id = {d['id']: d for d in c}
for item in g:
    item.update(c_by_id.get(item['id']), {})
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
0

This can be easily achieved by underscore functions _.uniq and _.union.

Just use this:

var finalData = _.uniq(_.union(c, g),  function (ele)  {
                    return ele.id
                })

This will return you what you are looking for.

Zac
  • 1,305
  • 3
  • 17
  • 28