Supprimer les objets en double de JSON Array

J'ai un tableau qui ressemble à ceci:

var standardsList = [ {"Grade": "Math K", "Domain": "Counting & Cardinality"}, {"Grade": "Math K", "Domain": "Counting & Cardinality"}, {"Grade": "Math K", "Domain": "Counting & Cardinality"}, {"Grade": "Math K", "Domain": "Counting & Cardinality"}, {"Grade": "Math K", "Domain": "Geometry"}, {"Grade": "Math 1", "Domain": "Counting & Cardinality"}, {"Grade": "Math 1", "Domain": "Counting & Cardinality"}, {"Grade": "Math 1", "Domain": "Orders of Operation"}, {"Grade": "Math 2", "Domain": "Geometry"}, {"Grade": "Math 2", "Domain": "Geometry"} ]; 

Et je dois supprimer les doublons pour que quelque chose comme ça reste:

 var standardsList = [ {"Grade": "Math K", "Domain": "Counting & Cardinality"}, {"Grade": "Math K", "Domain": "Geometry"}, {"Grade": "Math 1", "Domain": "Counting & Cardinality"}, {"Grade": "Math 1", "Domain": "Orders of Operation"}, {"Grade": "Math 2", "Domain": "Geometry"} ]; 

J'ai essayé d'installer underscore.js et j'utilise ._uniq mais cela ne semble fonctionner que si une seule key:value paire de key:value apparaît dans l'objet. Je n'arrive pas à le faire fonctionner sur plusieurs clés.

Lorsque j'essaie quelque chose comme:

 var uniqueStandards = _.uniq(standardsList, function(item, key, Domain){ return item.Domain; }); 

Je n'obtiens que les trois premières valeurs uniques (une par note). Mais j'ai besoin de toutes les valeurs uniques dans le domaine et le domaine. Existe-t-il un moyen simple d'alimenter les deux clés de la fonction _.uniq?

En fin de compte, j'ai besoin d'une liste avec chaque note unique comme l'en-tête et les domaines uniques comme éléments de la liste pour passer dans une page HTML. Je pourrais peut-être faire problème, alors, s'il existe un moyen plus facile d'atteindre cet objectif final, je suis ouvert aux idées.

Merci d'avance!

Modifier: Obtenir de bonnes réponses et voulait préciser quel était mon objectif final. J'essaie de créer une série de listes en HTML du formulaire:

 <div> <h3>Math K</h3> <li>Counting & Cardinality</li> <li>Geometry</li> </div> <div> <h3>Math 1</h3> <li>Counting & Cardinality</li> <li>Orders of Operation</li> </div> <div> <h3>Math 2</h3> <li>Geometry</li> </div> 

Mon original, cependant, était de créer un tableau et de le pousser dans l'élément <div> de la page avec $("#divid").append(array)

En fin de compte, j'ai besoin d'une liste avec chaque note unique comme l'en-tête et les domaines uniques comme éléments de la liste pour passer dans une page HTML. Je pourrais peut-être faire problème, alors, s'il existe un moyen plus facile d'atteindre cet objectif final, je suis ouvert aux idées.

Donc, vous n'avez pas vraiment besoin de ce tableau de résultats dans le format que vous avez demandé.

Dans ce cas, je réduis directement la chasse avec une solution très simple et efficace:

 var grades = {}; standardsList.forEach( function( item ) { var grade = grades[item.Grade] = grades[item.Grade] || {}; grade[item.Domain] = true; }); console.log( JSON.stringify( grades, null, 4 ) ); 

L'objet de grades résultant est:

 { "Math K": { "Counting & Cardinality": true, "Geometry": true }, "Math 1": { "Counting & Cardinality": true, "Orders of Operation": true }, "Math 2": { "Geometry": true } } 

Une chose intéressante à propos de cette approche est que c'est très rapide. Notez qu'il ne fait qu'une seule passe dans le tableau d'entrée, contrairement aux autres solutions qui nécessitent des passes multiples (que vous les écrivez ou si _.uniq() fait pour vous). Pour un petit nombre d'articles, cela ne sera pas important, mais il est bon de garder à l'esprit les listes plus importantes.

Et avec cet objet, vous avez maintenant tout ce dont vous avez besoin pour exécuter n'importe quel code ou générer tout autre format que vous voulez. Par exemple, si vous avez besoin du format de sortie de tableau exact que vous avez mentionné, vous pouvez utiliser:

 var outputList = []; for( var grade in grades ) { for( var domain in grades[grade] ) { outputList.push({ Grade: grade, Domain: domain }); } } JSON.stringify( outputList, null, 4 ); 

Cela enregistrera:

 [ { "Grade": "Math K", "Domain": "Counting & Cardinality" }, { "Grade": "Math K", "Domain": "Geometry" }, { "Grade": "Math 1", "Domain": "Counting & Cardinality" }, { "Grade": "Math 1", "Domain": "Orders of Operation" }, { "Grade": "Math 2", "Domain": "Geometry" } ] 

Rai demande dans un commentaire comment cette ligne de code fonctionne:

 var grade = grades[item.Grade] = grades[item.Grade] || {}; 

C'est un idiome commun pour récupérer une propriété d'objet ou fournir une valeur par défaut si la propriété est manquante. Notez que les affectations = sont effectuées dans l'ordre de droite à gauche. Nous pourrions donc le traduire littéralement pour utiliser une déclaration if et une variable temporelle:

 // Fetch grades[item.Grade] and save it in temp var temp = grades[item.Grade]; if( ! temp ) { // It was missing, so use an empty object as the default value temp = {}; } // Now save the result in grades[item.Grade] (in case it was missing) // and in grade grades[item.Grade] = temp; var grade = temp; 

Vous remarquerez peut-être que dans le cas où les grades[item.Grade] existent déjà, nous prenons la valeur que nous venons d'obtenir et la grades[item.Grade] dans la même propriété. Ceci est inutile, bien sûr, et vous ne le feriez probablement pas si vous écrivez le code comme ceci. Au lieu de cela, vous le simplifieriez:

 var grade = grades[item.Grade]; if( ! grade ) { grade = grades[item.Grade] = {}; } 

Ce serait un moyen parfaitement raisonnable d'écrire le même code, et il est plus efficace aussi. Cela vous permet également de faire un test plus spécifique que la "vérité" que le || L'idiome repose sur. Par exemple, au lieu de if( ! grade ) vous pouvez utiliser if( grade === undefined ) .

 function arrUnique(arr) { var cleaned = []; arr.forEach(function(itm) { var unique = true; cleaned.forEach(function(itm2) { if (_.isEqual(itm, itm2)) unique = false; }); if (unique) cleaned.push(itm); }); return cleaned; } var standardsList = arrUnique(standardsList); 

VIOLON

Cela renverra

 var standardsList = [ {"Grade": "Math K", "Domain": "Counting & Cardinality"}, {"Grade": "Math K", "Domain": "Geometry"}, {"Grade": "Math 1", "Domain": "Counting & Cardinality"}, {"Grade": "Math 1", "Domain": "Orders of Operation"}, {"Grade": "Math 2", "Domain": "Geometry"} ]; 

C'est exactement ce que vous avez demandé?

Solution Javascript pour votre cas:

 console.log(unique(standardsList)); function unique(obj){ var uniques=[]; var stringify={}; for(var i=0;i<obj.length;i++){ var keys=Object.keys(obj[i]); keys.sort(function(a,b) {return ab}); var str=''; for(var j=0;j<keys.length;j++){ str+= JSON.stringify(keys[j]); str+= JSON.stringify(obj[i][keys[j]]); } if(!stringify.hasOwnProperty(str)){ uniques.push(obj[i]); stringify[str]=true; } } return uniques; } 

Rétablissant une vieille question, mais je voulais publier une itération sur la réponse de @ adeneo. Cette réponse est complètement générale, mais pour ce cas d'utilisation, elle pourrait être plus efficace (c'est lent sur ma machine avec un ensemble de quelques milliers d'objets). Si vous connaissez les propriétés spécifiques des objets que vous devez comparer, il suffit de les comparer directement:

 var sl = standardsList; var out = []; for (var i = 0, l = sl.length; i < l; i++) { var unique = true; for (var j = 0, k = out.length; j < k; j++) { if ((sl[i].Grade === out[j].Grade) && (sl[i].Domain === out[j].Domain)) { unique = false; } } if (unique) { out.push(sl[i]); } } console.log(sl.length); // 10 console.log(out.length); // 5 

Ce qui suit fonctionne pour moi:

 _.uniq(standardsList, JSON.stringify) 

Cela serait probablement lent pour de très longues listes, cependant.

J'avais besoin de faire du déguisement des objets JSON, alors je me suis trouvé sur cette page. Cependant, je suis allé avec la solution ES6 courte (pas besoin de libs externes), en fonctionnant dans les extraits d'outils de développement de Chrome:

 const data = [ /* any list of objects */ ]; const set = new Set(data.map(item => JSON.stringify(item))); const dedup = [...set].map(item => JSON.parse(item)); console.log(`Removed ${data.length - dedup.length} elements`); console.log(dedup);