Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Merge duplicate objects in array of objects

Tags:

I have below array of objects,

var data = [     {         label: "Book1",         data: "US edition"     },     {         label: "Book1",         data: "UK edition"     },     {         label: "Book2",         data: "CAN edition"     } ]; 

I want to merge the duplicate objects based on attribute 'label' so that Final output will look like below,

var data = [     {         label: "Book1",         data: ["US edition", "UK edition"] //data attribute is merged     },     {         label: "Book2",         data: "CAN edition"     } ]; 

Can someone help me identify the approach?

like image 611
ScrapCode Avatar asked May 04 '15 08:05

ScrapCode


People also ask

How do you merge duplicate values in an array of objects?

To merge duplicate objects in array of objects with JavaScript, we can use the array map method. to merge the items with the label value together. To do this, we get an array of labels without the duplicates with [... new Set(data.

How do you combine two objects in an array?

Using the spread operator or the concat() method is the most optimal solution. If you are sure that all inputs to merge are arrays, use spread operator . In case you are unsure, use the concat() method. You can use the push() method to merge arrays when you want to change one of the input arrays to merge.

How do you remove duplicates from an array of objects?

To remove the duplicates from an array of objects:Use the Array. filter() method to filter the array of objects. Only include objects with unique IDs in the new array.

How do I combine multiple objects into one?

To merge objects into a new one that has all properties of the merged objects, you have two options: Use a spread operator ( ... ) Use the Object. assign() method.


1 Answers

I would probably loop through with filter, keeping track of a map of objects I'd seen before, along these lines (edited to reflect your agreeing that yes, it makes sense to make (entry).data always an array):

var seen = {}; data = data.filter(function(entry) {     var previous;      // Have we seen this label before?     if (seen.hasOwnProperty(entry.label)) {         // Yes, grab it and add this data to it         previous = seen[entry.label];         previous.data.push(entry.data);          // Don't keep this entry, we've merged it into the previous one         return false;     }      // entry.data probably isn't an array; make it one for consistency     if (!Array.isArray(entry.data)) {         entry.data = [entry.data];     }      // Remember that we've seen it     seen[entry.label] = entry;      // Keep this one, we'll merge any others that match into it     return true; }); 

In an ES6 environment, I'd use seen = new Map() rather than seen = {}.

Note: Array.isArray was defined by ES5, so some quite older browsers like IE8 won't have it. It can easily be shimmed/polyfilled, though:

if (!Array.isArray) {     Array.isArray = (function() {         var toString = Object.prototype.toString;         return function(a) {             return toString.call(a) === "[object Array]";         };     })(); } 

Side note: I'd probably also always make entry.data an array, even if I didn't see two values for it, because consistent data structures are easier to deal with. I didn't do that above because your end result showed data being just a string when there was only one matching entry. (We've done that above now.)

Live example (ES5 version):

var data = [      {          label: "Book1",          data: "US edition"      },      {          label: "Book1",          data: "UK edition"      },      {          label: "Book2",          data: "CAN edition"      }  ];  snippet.log("Before:");  snippet.log(JSON.stringify(data, null, 2), "pre");  var seen = {};  data = data.filter(function(entry) {      var previous;        // Have we seen this label before?      if (seen.hasOwnProperty(entry.label)) {          // Yes, grab it and add this data to it          previous = seen[entry.label];          previous.data.push(entry.data);            // Don't keep this entry, we've merged it into the previous one          return false;      }        // entry.data probably isn't an array; make it one for consistency      if (!Array.isArray(entry.data)) {          entry.data = [entry.data];      }        // Remember that we've seen it      seen[entry.label] = entry;        // Keep this one, we'll merge any others that match into it      return true;  });  snippet.log("After:");  snippet.log(JSON.stringify(data, null, 2), "pre");
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->  <script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
like image 150
T.J. Crowder Avatar answered Jan 02 '23 18:01

T.J. Crowder