Unisci oggetti duplicati nella matrice di oggetti

Ho sotto la matrice di oggetti,

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

Voglio unire gli oggetti duplicati in base all’attributo ‘etichetta’ in modo che l’output finale assomigli al seguente,

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

Qualcuno può aiutarmi a identificare l’approccio?

Probabilmente andrei in loop con il filter , tenendo traccia di una mappa di oggetti che avevo visto prima, lungo queste linee (modificato per riflettere il tuo accordo sul fatto che sì, ha senso rendere (entry).data sempre un 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 un ambiente ES6, userei seen = new Map() piuttosto che seen = {} .

Nota : Array.isArray stato definito da ES5, quindi alcuni browser più vecchi come IE8 non ce l’hanno. Può essere facilmente shimmed / polyfilled, tuttavia:

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

Nota a entry.data : probabilmente entry.data sempre entry.data come array, anche se non ho visto due valori, perché strutture di dati coerenti sono più facili da gestire. Non l’ho fatto sopra perché il risultato finale mostrava che i data erano solo una stringa quando c’era una sola voce corrispondente. (Lo abbiamo fatto sopra ora.)

Esempio dal vivo (versione ES5):

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

Questo codice è testato sull’ultima versione di Firefox. Per lavorare su altri browser cambia Array.isArray per una libreria come lodash o qualsiasi altra cosa tu preferisca.

 var data = [ { label: "Book1", data: "US edition" }, { label: "Book1", data: "UK edition" }, { label: "Book2", data: "CAN edition" } ], i = 0, j = data.length - 1, current; for (;i < data.length; i++) { current = data[i]; for (;j > i; j--) { if (current.label === data[j].label) { if (Array.isArray(current.data)) { current.data = current.data.concat([data[j].data]); } else { current.data = [].concat([data[j].data, current.data]); } data.splice(j, 1); } } } console.log(data);