'Combine json arrays by key, javascript

I need to combine two json arrays, delivered by two rest services. The entries with the same "id" belong together.

json1 = [{id:1,name:'aaa'},
     {id:5,name:'ccc'},
     {id:3,name:'bbb'}
   ];

 json2 = [{id:3,parameter1:'x', parameter2:'y', parameter3:'z'},
     {id:1,parameter1:'u', parameter2:'v', parameter3:'w'},
     {id:5,parameter1:'q', parameter2:'w', parameter3:'e'}
    ];

I need a combined/copied/cloned json array in javascript in the following way (my model in angular2):

json3 = [{id:3,name:'bbb',parameter1:'x', parameter2:'y',   parameter3:'z'},
     {id:1,name:'aaa', parameter1:'u', parameter2:'v', parameter3:'w'},
     {id:5,name:'ccc', parameter1:'q', parameter2:'w', parameter3:'e'}
    ];

Is there a way to combine them? The parameter names are not defined exactly and it needs to work with variable parameter vectors.

I tried it with mixed for each loops. Seems to me very ugly.



Solution 1:[1]

Two one-liners:

with lodash:

res = _(json1).concat(json2).groupBy('id').map(_.spread(_.assign)).value();

in ES2015:

res = json2.map(x => Object.assign(x, json1.find(y => y.id == x.id)));

Solution 2:[2]

ES2015 georg's answer works great;

    json1 = [
    {id:1, test: 0},
    {id:2, test: 0},
    {id:3, test: 0},
    {id:4, test: 0},
    {id:5, test: 0}
];

json2 = [
    {id:1, test: 1},
    {id:3, test: 1},
    {id:5, test: 1}
];

json1.map(x => Object.assign(x, json2.find(y => y.id == x.id)));

result:

{id:1, test: 1},
{id:2, test: 0},
{id:3, test: 1},
{id:4, test: 0},
{id:5, test: 1}

Solution 3:[3]

let json1 = [
  { id: 1, name: 'aaa' },
  { id: 5, name: 'ccc' },
  { id: 3, name: 'bbb' }
];

let json2 = [
  { id: 3, parameter1: 'x', parameter2: 'y', parameter3: 'z' },
  { id: 1, parameter1: 'u', parameter2: 'v', parameter3: 'w' },
  { id: 5, parameter1: 'q', parameter2: 'w', parameter3: 'e' }
];

let result = json1.map(obj => {
  let data = json2.find(item => item.id === obj.id);
  return {...obj, ...data}
});

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

Solution 4:[4]

JavaScript:

let json1 = [
  { id: 1, name: 'aaa' },
  { id: 5, name: 'ccc' },
  { id: 3, name: 'bbb' }
];

let json2 = [
  { id: 3, parameter1: 'x', parameter2: 'y', parameter3: 'z' },
  { id: 1, parameter1: 'u', parameter2: 'v', parameter3: 'w' },
  { id: 5, parameter1: 'q', parameter2: 'w', parameter3: 'e' }
];

let json3 = [];

json1.forEach((j1) => {
  json2.forEach((j2) => {
    if (j1.id === j2.id) {
      json3.push({ ...j1, ...j2 });
    }
  });
});

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

Solution 5:[5]

Here is a way where you first build an index keyed by id (sparse array) to detect and combine objects with matching id values, which then finally are concatenated back into a normal array:

json3 = json1.concat(json2).reduce(function(index, obj) {
    if (!index[obj.id]) {
        index[obj.id] = obj;
    } else {
        for (prop in obj) {
            index[obj.id][prop] = obj[prop];
        }
    }
    return index;
}, []).filter(function(res, obj) {
    return obj;
});

json1 = [
    {id:1,name:'aaa'},
    {id:5,name:'ccc'},
    {id:3,name:'bbb'}
];

json2 = [
    {id:3,parameter1:'x', parameter2:'y', parameter3:'z'},
    {id:1,parameter1:'u', parameter2:'v', parameter3:'w'},
    {id:5,parameter1:'q', parameter2:'w', parameter3:'e'}
];

json3 = json1.concat(json2).reduce(function(index, obj) {
    if (!index[obj.id]) {
        index[obj.id] = obj;
    } else {
        for (prop in obj) {
            index[obj.id][prop] = obj[prop];
        }
    }
    return index;
}, []).filter(function(res, obj) {
    return obj;
});

document.write('<pre>', JSON.stringify(json3, null, 4), '</pre>');

If your browser supports Object.assign:

json3 = json1.concat(json2).reduce(function(index, obj) {
    index[obj.id] = Object.assign({}, obj, index[obj.id]);
    return index;
}, []).filter(function(res, obj) {
    return obj;
});

Solution 6:[6]

Use forEach and filter we can resolve the requirement.

vehicleArray1 = [{id:1, name: "a"},{id:2, name: "b"},{id:3, name:"c"}];
vehicleArray2 = [{id:1, type: "two wheeler"},{id:2, type: "four wheeler"},{id:3, type:"six wheeler"}];
var outArr = [];
vehicleArray1.forEach(function(value) {
    var existing = vehicleArray2.filter(function(v, i) {
        return (v.id == value.id);
    });
    if (existing.length) {
        value.type = existing[0].type;
        outArr.push(value)
    } else {
        value.type = '';
        outArr.push(value);
    }
});
console.log(outArr)

Solution 7:[7]

Use nested loops to find the corresponding elements and merge them.

for (var i = 0; i < json1.length; i++) {
    var id = json1[i].id;
    for (var j = 0; j < json2.length; j++) {
        if (json2[j].id == id) {
            for (var key in json2[j]) {
                json1[i][key] = json2[j][key];
            }
            break;
        }
    }
}

At the end, json1 will contain the combined elements.

The above code assumes that every element of json2 matches something in json1. If there can be extra elements in json2, you'll need an additional loop afterward to copy those over to json1.

Solution 8:[8]

This should do it for you. I hope the code makes sense on its own. This example will always take the json1 value over the json2 value if both exist. If you want to change that then you need to switch the object references (src[i] and obj[j]) in the innermost loop.

// Will take src, and merge in the contents of obj.
// Expects an array of objects for both.
// Will keep src values in favour of obj values.
function extend(src, obj) {
  
  // Loop the src, in this case json1
  for (var i = 0; i < src.length; i++) {
    
    // For every loop of json1, also loop json2
    for (var j = 0; j < obj.length; j++) {
      
      // If we have matching IDs operate on this pair
      if (src[i].id == obj[j].id) {
          
        // For every key in the object being merged in,
        // if the key exists in src, ignore new value.
        // if the doesn't exist in src, take the new value.
        for (var key in obj[j]) {
          src[i][key] = src[i].hasOwnProperty(key) ? src[i][key] : obj[j][key];
        }
        
        // We found our matching pair, so break out of the json2 loop
        break;
        
      }
      
    }
    
  }
  
  return src;
}

// -------------------------------------------

var json1 = [{
  id: 1,
  name: 'aaa'
},{
  id: 5,
  name: 'ccc'
},{
  id: 3,
  name: 'bbb'
}];

var json2 = [{
  id: 3,
  parameter1: 'x', 
  parameter2: 'y', 
  parameter3: 'z'
},{
  id: 1,
  parameter1: 'u', 
  parameter2: 'v', 
  parameter3: 'w'
},{
  id: 5,
  parameter1: 'q', 
  parameter2: 'w', 
  parameter3: 'e'
}];

var json3 = extend(json1, json2);

// ---------------------------------------------

var pre = document.getElementById('out');
pre.innerHTML = JSON.stringify(json3);
<pre id="out"></pre>

Solution 9:[9]

Here is a generic solution using object-lib.

The advantage is that you get full control over how objects are merged and this supports nested and multiple nested groupBys.

// const objectLib = require('object-lib');

const { Merge } = objectLib;

const json1 = [{ id: 1, name: 'aaa' }, { id: 5, name: 'ccc' }, { id: 3, name: 'bbb' }];
const json2 = [{ id: 3, parameter1: 'x', parameter2: 'y', parameter3: 'z' }, { id: 1, parameter1: 'u', parameter2: 'v', parameter3: 'w' }, { id: 5, parameter1: 'q', parameter2: 'w', parameter3: 'e' }];

const groupById = Merge({ '[*]': 'id' });
console.log(groupById(json1, json2));
// => [ { id: 1, name: 'aaa', parameter1: 'u', parameter2: 'v', parameter3: 'w' }, { id: 5, name: 'ccc', parameter1: 'q', parameter2: 'w', parameter3: 'e' }, { id: 3, name: 'bbb', parameter1: 'x', parameter2: 'y', parameter3: 'z' } ]

const o1 = [{ id: 1, children: [{ type: 'A' }, { type: 'C' }] }, { id: 5 }];
const o2 = [{ id: 1, children: [{ type: 'A' }, { type: 'B' }] }, { id: 3 }];

console.log(groupById(o1, o2));
// => [ { id: 1, children: [ { type: 'A' }, { type: 'C' }, { type: 'A' }, { type: 'B' } ] }, { id: 5 }, { id: 3 } ]

const groupByCustom = Merge({
  '[*]': 'id',
  '[*].children[*]': 'type'
});
console.log(groupByCustom(o1, o2));
// => [ { id: 1, children: [ { type: 'A' }, { type: 'C' }, { type: 'B' } ] }, { id: 5 }, { id: 3 } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/[email protected]"></script>

Disclaimer: I'm the author of object-lib

Solution 10:[10]

Stack snippets of the concat-reduce answers

Result 1: using reduce and matching id's.
First concatenate the two arrays into a single array, then detect and combine objects with matching id's. Finally filter out all null values.

Result 2: using reduce and Object.assign.

const json1 = [{id:1,name:'aaa'},{id:3,name:'bbb'},{id:5,name:'ccc'}];
const json2 =
  [{id:3,parameters:'xyz'},{id:5,parameters:'qwe'},{id:1,parameters:'uvw'}];
const json7 = json1.concat(json2);
console.log(' json7:\n'+JSON.stringify(json7));

const Result_1 =
  json7.reduce((accumulator, obj) => {
    if (!accumulator[obj.id]) {
      accumulator[obj.id] = obj;
    } else {
      for (proprty in obj) { accumulator[obj.id][proprty] = obj[proprty];}
    }
    return accumulator;
  }, []).filter(x => x);
console.log('\n Result_1:\n' + JSON.stringify(Result_1));

const Result_2 =
  json1.concat(json2).reduce((accumulator, obj) => {
    accumulator[obj.id] = Object.assign({}, accumulator[obj.id], obj);
    return accumulator;
  }, []).filter(x => x);
console.log('\n Result_2:\n' + JSON.stringify(Result_2));
.as-console-wrapper { max-height: 100% !important; top: 0; }

References:

Solution 11:[11]

Stack snippets of the two one-liners

Lodash

const json1 = [{ id: 1, name: 'aaa' }, { id: 3, name: 'bbb' },
  { id: 5, name: 'ccc' }];
const json2 = [{ id: 3, parameters: 'xyz' },
  { id: 5, parameters: 'qwe' }, { id: 1, parameters: 'uvw' }];
console.log('json1 BEFORE:\n' + JSON.stringify(json1));

const result_Lodash =
  _(json1).concat(json2).groupBy('id').map(_.spread(_.assign)).value();
console.log('Result, Lodash:\n' + JSON.stringify(result_Lodash));
console.log('json1 AFTER:\n' + JSON.stringify(json1));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src=
"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.js"></script>

Object.assign

const json1 = [{ id: 1, name: 'aaa' }, { id: 3, name: 'bbb' },
  { id: 5, name: 'ccc' }];
const json2 = [{ id: 3, parameters: 'xyz' },
  { id: 5, parameters: 'qwe' }, { id: 1, parameters: 'uvw' }];
console.log('json2 BEFORE:\n' + JSON.stringify(json2));

const result_Object_assign =
  json2.map(x => Object.assign(x, json1.find(y => y.id === x.id)));
console.log('Object.assign:\n' + JSON.stringify(result_Object_assign));
console.log('json2 AFTER:\n' + JSON.stringify(json2));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src=
"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.js"></script>

It is worth pointing out that for both solutions, the contents of the first / left array are changed.
Run the snippets to see it.
For example, Object.assign(x, json1.find(y => y.id === x.id)) copies the compound data to the object x in json2, and the objects in json2 are thus updated via .map().
The identifier result_Object_assign is actually nothing but another pointer that points to the very same array as json2 points to - no new object is created!


Object.assign without changing the input arrays
If you don't want to change any of the input arrays, simply create a new array, as shown below:

const json1 = [{ id: 1, name: 'aaa' }, { id: 3, name: 'bbb' },
  { id: 5, name: 'ccc' }];
const json2 = [{ id: 3, parameters: 'xyz' },
  { id: 5, parameters: 'qwe' }, { id: 1, parameters: 'uvw' }];
console.log('json2 BEFORE:\n' + JSON.stringify(json2));

const result_Object_assign =
  json2.map(x => Object.assign({}, x, json1.find(y => y.id === x.id)));
console.log('Object.assign:\n' + JSON.stringify(result_Object_assign));
console.log('json2 AFTER:\n' + JSON.stringify(json2));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src=
"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.js"></script>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 georg
Solution 2 Killian Charlez
Solution 3 Mr. Polywhirl
Solution 4 Henke
Solution 5
Solution 6 KARTHIKEYAN.A
Solution 7 kujiy
Solution 8
Solution 9
Solution 10
Solution 11