Dve Dve - 4 months ago 12
Javascript Question

Merging/extend javascript object arrays based on join of a key property in each

I am wanting to merge the following object arrays, by first joining on the id property

var arr1 = [{
id: 1,
name: 'fred',
title: 'boss'
},{
id: 2,
name: 'jim',
title: 'nobody'
},{
id: 3,
name: 'bob',
title: 'dancer'
}];

var arr2 = [{
id: 1,
wage: '300',
rate: 'day'
},{
id: 2,
wage: '10',
rate: 'hour'
},{
id: 3,
wage: '500',
rate: 'week'
}];


So the result would be

[{
id: 1,
name: 'fred',
title: 'boss',
wage: '300',
rate: 'day'
},{
id: 2,
name: 'jim',
title: 'nobody',
wage: '10',
rate: 'hour'
},{
id: 3,
name: 'bob',
title: 'dancer',
wage: '500',
rate: 'week'
}]


I would like to avoid using js frameworks (if possible), although ExtJs is already part of the project.
AT the moment I have a loop with an inner loop that if the keys match it copies the properties and breaks out of the inner loop to start the next outer loop.

Any better suggestions?

Answer

Like this?

var combined = [];
function findSecond(id,second){
    for (var i=0;i<second.length;i++){
        if(second[i].id === id){
            return second[i];
        }
    }
    return null
}

while (el = arr1.pop()){
    var getSec = findSecond(el.id,arr2);
    if (getSec){
        for (var l in getSec){
            if (!(l in el)) {
                el[l] = getSec[l];
            }
        }
        combined.push(el);
    }
}

If the arrays have the same length, and the id's are equal, a simpler merge will do:

function merge(a1,a2) {
    var i = -1;
    while ((i = i+1)<a1.length)  {
     for (var l in a2[i]) {
            if (!(l in a1[i] )) {
                a1[i][l] = a2[i][l];
            }
     }
    }
   return a1; 
}

Here's a working example

[Edit 2016/07/30] Added a snippet using more functional approach and, based on @djangos comment an extra method to combine both arrays.

(function() {
    var arr1 = [{
        id: 1,
        name: 'fred',
        title: 'boss'
    },{
        id: 2,
        name: 'jim',
        title: 'nobody'
    },{
        id: 3,
        name: 'bob',
        title: 'dancer'
    }];

    var arr2 = [{
        id: 1,
        wage: '300',
        rate: 'day'
    },{
        id: 2,
        wage: '10',
        rate: 'hour'
    } ,{
        id: 4,
        wage: '500',
        rate: 'week'
    }];

    var alert = function(str) {document.querySelector('#result').textContent += str + '\n';};
    alert('combine:')
    alert(JSON.stringify(combine(arr1, arr2), null, ' '));
    alert('\ncombine both:')
    alert(JSON.stringify(combineBoth(arr1, arr2), null, ' '));
  
    function combine(arr1, arr2) {
      return arr1.map(
          function (el) {
                var findInB = this.filter(function (x) {return x.id === el.id;});
                if (findInB.length) {
                    var current = findInB[0];
                    for (var l in current) {
                        if (!el[l]) {el[l] = current[l];}
                    }
                }
                return el;
          }, arr2);
    }

    function combineBoth(arr1, arr2) {
        var combined = arr1.map(
            function (el) {
                var findInB = this.filter(function (x) {return x.id === el.id;});
                if (findInB.length) {
                    var current = findInB[0];
                    for (var l in current) {
                        if (!el[l]) {el[l] = current[l];}
                    }
                }
                return el;
            }, arr2);
        combined = combined.concat(arr2.filter(
            function (el) {
                return !this.filter(function (x) {return x.id === el.id;}).length;
            }, combined));
        return combined;
    }
}());
<pre id="result"></pre>

Comments