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?
Advertisement
Answer
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>