最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Combine json arrays by key, javascript - Stack Overflow

programmeradmin8浏览0评论

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
  • Possible duplicate of How to merge two arrays in Javascript and de-duplicate items – Luke Commented Mar 9, 2016 at 22:22
  • 2 @Doctus, that question does not deal with nested properties. – trincot 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. – user2083142 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. – weaknespase Commented Mar 9, 2016 at 22:42
  • Possible duplicate of Merge array of javascript objects by property key – Heretic Monkey Commented Mar 9, 2016 at 22:44
 |  Show 2 more comments

12 Answers 12

Reset to default 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)));

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>

发布评论

评论列表(0)

  1. 暂无评论