0

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.

1
  • 1
    please post the code that you have tried Commented Mar 6, 2019 at 15:01

3 Answers 3

4

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)

Sign up to request clarification or add additional context in comments.

1 Comment

Sorry it took so long. This solution worked great for me!! Than you so much!
1

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

Comments

1

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

2 Comments

Actually, reduce isn't perfect many people look down on it. It's often preferable to use a simple for loop instead of reduce. +1
I agree @kemicofa. I've strayed away from lots of clever map/reduce blocks in favor of syntax like the example above. Map and reduce are still used in my code when appropriate, but I find this easier to come back to at a later date and grok quickly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.