Reputation: 3
I'm having some troubles merging an array of JSON objects together by key. Would anyone have a good solution to this using just Javascript?
{
"code": "12345",
"error": "12345 error 1"
},
{
"code": "12345",
"error": "12345 error 2"
},
{
"code": "67890",
"error": "67890 error 1"
},
{
"code": "67890",
"error": "67890 error 2"
},
{
"code": "67890",
"error": "67890 error 3"
},
{
"code": "67890",
"error": "67890 error 4"
},
{
"code": "67890",
"error": "67890 error 5"
},
{
"code": "12092",
"error": "12092 error 1"
},
{
"code": "12092",
"error": "12092 error 2"
}
Should be transformed to
{
"code": "12345",
"error": "12345 error 1, 12345 error 2"
},
{
"code": "67890",
"error": "67890 error 1, 67890 error 2, 67890 error 3, 67890 error 4, 67890 error 5"
},
{
"code": "12092",
"error": "12092 error 1, 12092 error 2"
}
Any help would be greatly appreciated. I've racked my brain over this for a long time and just cant seem to get it down.
Upvotes: 0
Views: 70
Reputation: 50346
Use array reduce
to create the new merged array of objects. Inside the callback function check if the accumulator array have an object which already have the code
. For this use findIndex
. If the code matches then update the error
. Else push the current object in the accumulator
const data = [{"code":"12345","error":"12345 error 1"},{"code":"12345","error":"12345 error 2"},{"code":"67890","error":"67890 error 1"},{"code":"67890","error":"67890 error 2"},{"code":"67890","error":"67890 error 3"},{"code":"67890","error":"67890 error 4"},{"code":"67890","error":"67890 error 5"},{"code":"12092","error":"12092 error 1"},{"code":"12092","error":"12092 error 2"}];
let mergedData = data.reduce(function(acc, curr) {
let findIndex = acc.findIndex(function(item) {
return item.code === curr.code;
})
if (findIndex === -1) {
acc.push(curr)
} else {
acc[findIndex].error += ', ' + curr.error
}
return acc;
}, []);
console.log(mergedData)
Upvotes: 4
Reputation: 8246
The existing answer using Reduce and checking for the existing object key is perfect.
Here's another method of doing this using Map and other ES6 syntax (for...of
) that I think is easy to read and just another interesting approach:
const data = [{"code":"12345","error":"12345 error 1"},{"code":"12345","error":"12345 error 2"},{"code":"67890","error":"67890 error 1"},{"code":"67890","error":"67890 error 2"},{"code":"67890","error":"67890 error 3"},{"code":"67890","error":"67890 error 4"},{"code":"67890","error":"67890 error 5"},{"code":"12092","error":"12092 error 1"},{"code":"12092","error":"12092 error 2"}];
// Define a new empty Map.
const map = new Map();
// Loop through all data objects.
for (const dataEntry of data) {
// Check if we've already added this to our map; if not, add blank entry.
if (!map.has(dataEntry.code)) {
map.set(dataEntry.code, {code: dataEntry.code,error:[]});
}
// Append to the error property.
const mapEntry = map.get(dataEntry.code);
mapEntry.error.push(dataEntry.error);
}
// Flatten the error entries to be a single String instead of an Array.
for (const entry of map.values()) {
entry.error = entry.error.join(', ');
}
const output = Array.from(map.values());
console.log(output);
Upvotes: 1
Reputation: 17636
Using Array#from, Array#reduce, Array#map and Map you could do somethign like this.
Idea is to first regroup everything with the Array#reduce and Map, and then to transform the data to the output you seek using Array#map.
const data = [{"code":"12345","error":"12345 error 1"},{"code":"12345","error":"12345 error 2"},{"code":"67890","error":"67890 error 1"},{"code":"67890","error":"67890 error 2"},{"code":"67890","error":"67890 error 3"},{"code":"67890","error":"67890 error 4"},{"code":"67890","error":"67890 error 5"},{"code":"12092","error":"12092 error 1"},{"code":"12092","error":"12092 error 2"}];
const res = Array.from(
data.reduce((a,{code, error})=>{
return a.set(code, [error].concat(a.get(code)||[]))
}, new Map())
).map(([code, error])=>({code, error: error.join(",")}));
console.log(res);
Upvotes: 1