Nora
Nora

Reputation: 186

Combining objects with duplicate values inside an array

If I have an array of objects and there are certain objects that have the same name/property (in my case there are 2 instances of objects with a name of "highway"), how can I remove these objects from the array and add a new object with that contains data from both?

const data = [
    { "data": [ { "x": "sensor_error_code", "y": [ 0, 9 ] } ], "name": 123 },
    { "data": [ { "x": "road_type", "y": [ 15, 24 ] } ], "name": "city" },
    { "data": [ { "x": "road_type", "y": [ 0, 14 ] } ], "name": "highway" },
    { "data": [ { "x": "road_type", "y": [ 25, 30 ] } ], "name": "highway" },
    { "data": [ { "x": "weather", "y": [ 0, 8 ] } ], "name": "rain" },
    { "data": [ { "x": "weather", "y": [ 9, 24 ] } ], "name": "sun" },
    { "data": [ { "x": "special_object", "y": [ 5, 15 ] } ], "name": true }
];

e.g. having one instance of "highway" with a data object containing the values from both:

{ "data": [ { "x": "road_type", "y": [ 0, 14 ] }, { "x": "road_type", "y": [ 25, 30 ] } ], "name": "highway" }

So far I've tried to make a new empty object and loop over the data array, accumulating that empty object with names and values from that array. I was planning on converting that object to an array in the end. The code I've used is below:

const myData = {};
atts.forEach(att => {
  if (myData[att.name]) {
    myData[att.name].push(att.data)
  } else {
    myData[att.name] = att.data;
  }
})
console.log(myData)

However, the code I've used above involves some additional manipulation to make the data ready for consumption by a library and I was wondering if there are simpler ways to achieve my goal?

Upvotes: 1

Views: 49

Answers (3)

Kinglish
Kinglish

Reputation: 23654

A different approach using reduce - tracking the items by keys and merging the arrays of duplicate objects

const combined = Object.entries(data.reduce((b,a) => {
 // our result will be an object so wrap it in Object.entries so we can iterate through to finish
    if (b.hasOwnProperty(a.name)) b[a.name].data = [...b[a.name].data, ...a.data];
    // if the property exists, merge the 'y' array
    else b[a.name]=a;
    // otherwise add to the object
    return b;
    },{})).map(e => e[1])
    // finally map the result out to the desired format

const data = [
    { "data": [ { "x": "sensor_error_code", "y": [ 0, 9 ] } ], "name": 123 },
    { "data": [ { "x": "road_type", "y": [ 15, 24 ] } ], "name": "city" },
    { "data": [ { "x": "road_type", "y": [ 0, 14 ] } ], "name": "highway" },
    { "data": [ { "x": "road_type", "y": [ 25, 30 ] } ], "name": "highway" },
    { "data": [ { "x": "weather", "y": [ 0, 8 ] } ], "name": "rain" },
    { "data": [ { "x": "weather", "y": [ 9, 24 ] } ], "name": "sun" },
    { "data": [ { "x": "special_object", "y": [ 5, 15 ] } ], "name": true }
];

const combined = Object.entries(data.reduce((b,a) => {
if (b.hasOwnProperty(a.name)) b[a.name].data[0].y = [...b[a.name].data[0].y, ...a.data[0].y];
else b[a.name]=a;
return b;
},{})).map(e => e[1])

console.log(combined)

Upvotes: 1

Kelvin Schoofs
Kelvin Schoofs

Reputation: 8718

Your solution has a decent start. There are only 2 problems with your solution:

  • You need to "unwrap" the array when pushing it
  • You need to combine them all together at the end
const myData = {};
atts.forEach(att => {
  if (myData[att.name]) {
    myData[att.name].push(...att.data) // Notice this change
  } else {
    myData[att.name] = att.data;
    // or if you don't want to alter the original data
    myData[att.name] = [...att.data]; // basically a shallow copy
  }
})
const result = [];
for (const key in myData) {
    result.push({ data: myData[key], name: key });
}
console.log(result);

Upvotes: 1

Tushar Shahi
Tushar Shahi

Reputation: 20441

You can use .reduce() (to convert your array into an aggregated value) along with .findIndex() (this helps in finding if data from same category exists in the new array you are making)

const atts = [
    { "data": [ { "x": "sensor_error_code", "y": [ 0, 9 ] } ], "name": 123 },
    { "data": [ { "x": "road_type", "y": [ 15, 24 ] } ], "name": "city" },
    { "data": [ { "x": "road_type", "y": [ 0, 14 ] } ], "name": "highway" },
    { "data": [ { "x": "road_type", "y": [ 25, 30 ] } ], "name": "highway" },
    { "data": [ { "x": "weather", "y": [ 0, 8 ] } ], "name": "rain" },
    { "data": [ { "x": "weather", "y": [ 9, 24 ] } ], "name": "sun" },
    { "data": [ { "x": "special_object", "y": [ 5, 15 ] } ], "name": true }
];


let myData = atts.reduce((agg, x) => {
  let isIndex = agg.findIndex(a => a.name == x.name);
  if(isIndex > -1) agg[isIndex].data.push(x.data[0]);
else {
agg.push({data : x.data , name : x.name});
}
return agg;
},[]);
console.log(myData)

Upvotes: 1

Related Questions