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.
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.
Share Improve this question edited Dec 25, 2017 at 13:01 KARTHIKEYAN.A 20k8 gold badges135 silver badges149 bronze badges asked Mar 9, 2016 at 22:20 user2083142user2083142 4411 gold badge6 silver badges16 bronze badges 7 | Show 2 more comments12 Answers
Reset to default 59Two 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)));
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}
If you wanted to write it so that you could take in any number of arrays, not just 2, you could utilize arguments, and do something like this:
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'}];
function joinObjects() {
var idMap = {};
// Iterate over arguments
for(var i = 0; i < arguments.length; i++) {
// Iterate over individual argument arrays (aka json1, json2)
for(var j = 0; j < arguments[i].length; j++) {
var currentID = arguments[i][j]['id'];
if(!idMap[currentID]) {
idMap[currentID] = {};
}
// Iterate over properties of objects in arrays (aka id, name, etc.)
for(key in arguments[i][j]) {
idMap[currentID][key] = arguments[i][j][key];
}
}
}
// push properties of idMap into an array
var newArray = [];
for(property in idMap) {
newArray.push(idMap[property]);
}
return newArray;
}
var json3 = joinObjects(json1, json2);
console.log(JSON.stringify(json3));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Here is a working codepen.
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; }
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; }
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;
});
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)
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
.
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>
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
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:
- Array.prototype.reduce
- Object.assign
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>
forEach
with arrow functions - it would look a bit better. – weaknespase Commented Mar 9, 2016 at 22:42