26

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.

7
  • Possible duplicate of How to merge two arrays in Javascript and de-duplicate items Commented Mar 9, 2016 at 22:22
  • 2
    @Doctus, that question does not deal with nested properties. Commented Mar 9, 2016 at 22:24
  • @Doctus, i think my question is different. I do not just want to combine and eliminate double entries, i want to merge the key-value pairs of both json arrays, based on a unique key in the json objects. Commented Mar 9, 2016 at 22:27
  • > I tried it with mixed for each loops. Seems to me very ugly. - try forEach with arrow functions - it would look a bit better. Commented Mar 9, 2016 at 22:42
  • Possible duplicate of Merge array of javascript objects by property key Commented Mar 9, 2016 at 22:44

12 Answers 12

59

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)));
Sign up to request clarification or add additional context in comments.

6 Comments

I wrote a set of functions for this last night in 8 lines and about 2 hrs. I used only map and filter. How the hell did I forget that there are other Array methods?! Upvote for Object.assign though didn't think of that either. Used a spread operator. Yours is sorter and easier to read.
Is there an equivalent in underscore?
Is there a way to merge the new items from json2 also, not only that exists on both?
@Joel I just found this sample of outerJoin using Ramda: github.com/ramda/ramda/wiki/Cookbook#sql-style-joins const joinOuter = R.curry((f1, f2, t1, t2) => { let o1 = R.indexBy(f1, t1); let o2 = R.indexBy(f2, t2); return R.values(R.mergeWith(R.merge, o1, o2)); }); // usage: joinOuter(R.prop('id'), R.prop('buyer'), people, transactions) // result: // [{ id: 1, name: 'me', buyer: 1, seller: 10 }, // { buyer: 2, seller: 5 }, // { id: 3, name: 'you' }]
I have found that the Lodash solution is a full outer join, meaning each item from both json1 and json2 are merged into the result - irrespective of whether the item is also in the other array. The Object.assign solution is a left outer join, meaning all items from json1 go into the result, but items in json2 are joined into the result only if they also exist in json1. For a solution giving a true inner join, see this answer.
|
11

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}

1 Comment

This doesn't achieve what was asked in the Question.
6

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.

Comments

5

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; }

1 Comment

A nicer looking answer than many here. You might want to make this a stack snippet.
2

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; }

1 Comment

This is a fine answer, demonstrating a true inner join of the two arrays. In fact, I have not seen any other answer to this question that does exactly that. Nor among the answers to the questions Merge two array of objects based on a key or How can I perform an inner join with two object arrays in JavaScript?. (The last question verbally asks for an inner join, but by the description and the output example it is actually a question asking for a full outer join.)
1

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;
});

Comments

1

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)

Comments

1

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.

2 Comments

on large data set it would be more effective to convert smaller array into sparse array (by using id as index) and cut nested loop.
Yeah, that's how I usually do it, I decided to write the quick-and-dirty version this time.
0

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>

Comments

0

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

Comments

0

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:

Comments

0

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>

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.