Suppression des objets en double avec Underscore pour Javascript

124

J'ai ce genre de tableau:

var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];

Je voudrais le filtrer pour avoir:

var bar = [ { "a" : "1" }, { "b" : "2" }];

J'ai essayé d'utiliser _.uniq, mais je suppose que parce que ce { "a" : "1" }n'est pas égal à lui-même, cela ne fonctionne pas. Existe-t-il un moyen de fournir un soulignement uniq avec une fonction de substitution égale?

plus-
la source
S'il vous plaît poster votre code aussi
Chetter Hummin
Des choses comme { "a" : "2" }existent-elles? Si oui, est-ce l'attribut ou la valeur qui le rend unique?
Matt
Oui, j'ai un attribut comme clé, j'ai implémenté l'index que quelqu'un m'a montré sur un autre sujet, mais j'ai ensuite voulu nettoyer mon code en utilisant des bibliothèques courantes
plus-
1
Veuillez modifier la réponse acceptée.
Vadorequest le

Réponses:

232

.uniq / .unique accepte un rappel

var list = [{a:1,b:5},{a:1,c:5},{a:2},{a:3},{a:4},{a:3},{a:2}];

var uniqueList = _.uniq(list, function(item, key, a) { 
    return item.a;
});

// uniqueList = [Object {a=1, b=5}, Object {a=2}, Object {a=3}, Object {a=4}]

Remarques:

  1. Valeur de retour de rappel utilisée pour la comparaison
  2. Premier objet de comparaison avec une valeur de retour unique utilisée comme unique
  3. underscorejs.org ne démontre aucune utilisation de rappel
  4. lodash.com montre l'utilisation

Autre exemple: utiliser le callback pour extraire les marques de voitures, les couleurs d'une liste

Shanimal
la source
falsen'est pas nécessaire pour _.uniq(). Aussi dans lodash, vous auriez pu l'écrire comme ça _.uniq(a, 'a');, car il épilera la propriété asur les objets.
Larry Battle
Le raccourci "'_.pluck' callback" ne fonctionne que si vous passez une valeur pour isSorted (par exemple _.uniq(a, false, 'a')) j'ai envoyé un ping à github / bestiejs / lodash et ils ont dit que le problème était résolu sur le bord. Donc, si vous n'utilisez pas de fonction, assurez-vous d'avoir la dernière. Cela peut ne pas être un problème pour le trait de soulignement.
Shanimal
2
Iterator ne sonne pas comme un bon nom, c'est une fonction de hachage qui sera de déterminer l'identité de chaque objet
Juan Mendes
Modifié pour utiliser le rappel pour être plus cohérent avec la documentation de lodash :)
Shanimal
1
Votre exemple à jsbin peut avoir une mise à jour. (1) Donne: _ (voitures) .uniq ('make'). Map ('make'). ValueOf () AND (2) Colors: _ (cars) .uniq ('color'). Map ('color' ).valeur de(). Vous pouvez faire mûrir la couleur et faire des fermetures. (Tout cela si vous mettez à niveau le lodash utilisé)
Vitor Tyburski
38

Si vous cherchez à supprimer les doublons en fonction d'un identifiant, vous pouvez faire quelque chose comme ceci:

var res = [
  {id: 1, content: 'heeey'},
  {id: 2, content: 'woah'}, 
  {id: 1, content:'foo'},
  {id: 1, content: 'heeey'},
];
var uniques = _.map(_.groupBy(res,function(doc){
  return doc.id;
}),function(grouped){
  return grouped[0];
});

//uniques
//[{id: 1, content: 'heeey'},{id: 2, content: 'woah'}]
Petter
la source
La réponse acceptée ne fonctionne pas lorsque l'identifiant unique est un Date. Mais c'est le cas.
gunwin
17

Mise en œuvre de la réponse de Shiplu.

var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];

var x = _.uniq( _.collect( foo, function( x ){
    return JSON.stringify( x );
}));

console.log( x ); // returns [ { "a" : "1" }, { "b" : "2" } ]
Bataille de Larry
la source
btw, comment avez-vous obtenu 4 votes positifs? Pour obtenir les propriétés de votre résultat, vous devez reconvertir chaque valeur de tableau en un objet. Quelque chose comme JSON.parse(x[0]).aparce que x n'est pas un tableau d'objets, c'est un tableau de chaînes. De plus, si vous ajoutez des valeurs b aux uniques et inversez l'ordre de a / b, elles ne sont plus considérées comme uniques par votre fonction. (par exemple, "" {\ "a \": \ "1 \", \ "b \": 2} "! =" {\ "b \": 2, \ "a \": \ "1 \"} ") Il me manque peut-être quelque chose, mais le résultat ne devrait-il pas au moins être utile? Voici un jsbin pour illustrer jsbin.com/utoruz/2/edit
Shanimal
1
Vous avez raison, la condition d'avoir les mêmes clés mais dans un ordre différent rompt l'implémentation. Mais je ne sais pas pourquoi vous ne vérifiez la clé que apour chaque objet, alors qu'il pourrait y avoir des objets en double ne contenant pas la clé a. Cependant, il serait logique que ce asoit un identifiant unique.
Larry Battle
Lorsque je répondais à la question, il m'a semblé que l'objectif de la question était de passer outre (a ==(=) b when a = b = {a:1}). Le point de ma réponse était l'itérateur. J'ai essayé de répondre sans me soucier du motif, qui pourrait être n'importe quoi, non? (par exemple, ils voulaient peut-être extraire une liste de marques, de couleurs d'une liste de voitures dans un spectacle. jsbin.com/evodub/2/edit ) Bravo !
Shanimal
Je pense également que cela nous aide à donner des réponses concises lorsque quelqu'un qui pose une question fournit un motif. C'est une course, donc je préfère être le premier et clarifier si nécessaire. Joyeuse Saint Patrick.
Shanimal
Eh bien, je viens de donner un autre vote pour parce que cela répond à ma question sur la comparaison des tableaux imbriqués. Cherchait seulement comment remplacer leiterator
nevi_me
15

Quand j'ai un identifiant d'attribut, c'est ma manière préférée de soulignement:

var x = [{i:2}, {i:2, x:42}, {i:4}, {i:3}];
_.chain(x).indexBy("i").values().value();
// > [{i:2, x:42}, {i:4}, {i:3}]
tuxbear
la source
12

L'utilisation de la librairie unique de soulignement qui suit fonctionne pour moi, je rend la liste unique en fonction de _id, puis renvoie la valeur String de _id:

var uniqueEntities = _.uniq(entities, function (item, key, a) {
                                    return item._id.toString();
                                });
Aqib Mumtaz
la source
10

Voici une solution simple, qui utilise une comparaison d'objets approfondie pour vérifier les doublons (sans recourir à la conversion en JSON, ce qui est inefficace et piraté)

var newArr = _.filter(oldArr, function (element, index) {
    // tests if the element has a duplicate in the rest of the array
    for(index += 1; index < oldArr.length; index += 1) {
        if (_.isEqual(element, oldArr[index])) {
            return false;
        }
    }
    return true;
});

Il filtre tous les éléments s'ils ont un doublon plus tard dans le tableau - de sorte que le dernier élément en double est conservé.

Le test des utilisations en double _.isEqualqui effectue une comparaison approfondie optimisée entre les deux objets, consultez la documentation isEqual de soulignement pour plus d'informations.

edit: mis à jour pour utiliser _.filterce qui est une approche plus propre

Joshua Bambrick
la source
Cela ne dépend pas d'avoir une propriété unique prédéfinie? Je l'aime.
Don McCurdy
1
Une bonne solution pour un petit tableau d'objets, mais une boucle dans une boucle est coûteuse par rapport à la fourniture d'un identifiant uniq.
penner
7

Essayez la fonction d'itérateur

Par exemple, vous pouvez retourner le premier élément

x = [['a',1],['b',2],['a',1]]

_.uniq(x,false,function(i){  

   return i[0]   //'a','b'

})

=> [['a', 1], ['b', 2]]

IvanM
la source
l'argument des secondes est en fait facultatif, vous pouvez également le faire_.uniq(x,function(i){ return i[0]; });
jakecraige
3

voici ma solution (coffeescript):

_.mixin
  deepUniq: (coll) ->
    result = []
    remove_first_el_duplicates = (coll2) ->

      rest = _.rest(coll2)
      first = _.first(coll2)
      result.push first
      equalsFirst = (el) -> _.isEqual(el,first)

      newColl = _.reject rest, equalsFirst

      unless _.isEmpty newColl
        remove_first_el_duplicates newColl

    remove_first_el_duplicates(coll)
    result

exemple:

_.deepUniq([ {a:1,b:12}, [ 2, 1, 2, 1 ], [ 1, 2, 1, 2 ],[ 2, 1, 2, 1 ], {a:1,b:12} ]) 
//=> [ { a: 1, b: 12 }, [ 2, 1, 2, 1 ], [ 1, 2, 1, 2 ] ]
Szymanowski
la source
3

avec un soulignement, j'ai dû utiliser String () dans la fonction iteratee

function isUniq(item) {
    return String(item.user);
}
var myUniqArray = _.uniq(myArray, isUniq);
dam1
la source
0

Je voulais résoudre cette solution simple d'une manière simple d'écrire, avec un peu de peine de frais de calcul ... mais n'est-ce pas une solution triviale avec une définition de variable minimale, n'est-ce pas?

function uniq(ArrayObjects){
  var out = []
  ArrayObjects.map(obj => {
    if(_.every(out, outobj => !_.isEqual(obj, outobj))) out.push(obj)
  })
  return out
}
Junji Shimagaki
la source
0
var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var bar = _.map(_.groupBy(foo, function (f) { 
        return JSON.stringify(f); 
    }), function (gr) { 
        return gr[0]; 
    }
);

Permet de décomposer cela. Commençons par regrouper les éléments du tableau par leur valeur stringifiée

var grouped = _.groupBy(foo, function (f) { 
    return JSON.stringify(f); 
});

grouped ressemble à:

{
    '{ "a" : "1" }' = [ { "a" : "1" } { "a" : "1" } ],
    '{ "b" : "2" }' = [ { "b" : "2" } ]
}

Puis attrapons le premier élément de chaque groupe

var bar = _.map(grouped, function(gr)
    return gr[0]; 
});

bar ressemble à: [ { "a" : "1" }, { "b" : "2" } ]

Mets le tout ensemble:

var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var bar = _.map(_.groupBy(foo, function (f) { 
        return JSON.stringify(f); 
    }), function (gr) { 
        return gr[0]; 
    }
);
Kelly Bigley
la source
3
Bienvenue dans stackoverflow. En plus du code que vous avez fourni, essayez d'expliquer pourquoi et comment cela résout le problème.
jtate
bon appel. Merci. Mis à jour avec une description de la façon dont cela fonctionne.
Kelly Bigley
-5

Vous pouvez le faire en abrégé comme:

_.uniq(foo, 'a')

nnattawat
la source
votre solution ne fonctionne pas pour les tableaux d'objets, mais juste pour les tableaux
Toucouleur