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?
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.
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.
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.
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.
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 (We've done that above now.)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.
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With