Entfernen doppelter Objekte mit Unterstrich für Javascript


124

Ich habe diese Art von Array:

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

Ich möchte es filtern, um zu haben:

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

Ich habe versucht, _.uniq zu verwenden, aber ich denke, weil { "a" : "1" }es nicht gleich ist, funktioniert es nicht. Gibt es eine Möglichkeit, Unterstrich-Uniq mit einer Overriden-Equals-Funktion zu versehen?


Bitte posten Sie auch Ihren Code
Chetter Hummin

Gibt { "a" : "2" }es solche Dinge ? Wenn ja, ist es das Attribut oder der Wert, der es einzigartig macht?
Matt

Ja, ich habe ein Attribut als Schlüssel. Ich habe den Index implementiert, den mir jemand zu einem anderen Thema gezeigt hat, aber dann wollte ich meinen Code mithilfe einiger gängiger Bibliotheken
bereinigen

1
Bitte ändern Sie die akzeptierte Antwort.
Vadorequest

Antworten:


232

.uniq / .unique akzeptiert einen Rückruf

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}]

Anmerkungen:

  1. Rückrufrückgabewert zum Vergleich
  2. Erstes Vergleichsobjekt mit eindeutigem Rückgabewert als eindeutig verwendet
  3. underscorejs.org zeigt keine Rückrufnutzung
  4. lodash.com zeigt die Nutzung

Ein weiteres Beispiel: Verwenden des Rückrufs zum Extrahieren von Automarken und Farben aus einer Liste


falseist nicht erforderlich für _.uniq(). Auch in lodash hätte man es so schreiben können _.uniq(a, 'a');, da es das Eigentum aan den Objekten zupft .
Larry Battle

Die Rückrufkürzel "'_.pluck'" funktioniert nur, wenn Sie einen Wert für isSorted übergeben (z. B. _.uniq(a, false, 'a')). Ich habe github / bestiejs / lodash gepingt und sie sagten, das Problem sei am Rande behoben worden. Wenn Sie also keine Funktion verwenden, stellen Sie sicher, dass Sie über die neueste Funktion verfügen. Dies ist möglicherweise kein Problem für den Unterstrich.
Shanimal

2
Iterator klingt nicht nach einem guten Namen, es ist eine Hash-ähnliche Funktion, die darin besteht, die Identität jedes Objekts zu bestimmen
Juan Mendes

Bearbeitet, um Rückruf zu verwenden, um konsistenter mit lodash docs zu sein :)
Shanimal

1
Ihr Beispiel bei jsbin kann ein Update haben. (1) Hersteller: _ (Autos) .uniq ('make'). Map ('make'). ValueOf () AND (2) Farben: _ (Autos) .uniq ('color'). Map ('color') ).Wert von(). Sie können die Farbe abreiben und Verschlüsse machen. (All das, wenn Sie de lodash verwendet upgraden)
Vitor Tyburski

38

Wenn Sie Duplikate basierend auf einer ID entfernen möchten, können Sie Folgendes tun:

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'}]

Die akzeptierte Antwort funktioniert nicht, wenn die eindeutige Kennung a ist Date. Dies ist jedoch der Fall.
Gunwin

17

Umsetzung der Antwort von 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" } ]

Übrigens, wie hast du 4 Upvotes bekommen? Um zu den Eigenschaften Ihres Ergebnisses zu gelangen, müssen Sie jeden Array-Wert wieder in ein Objekt zurücksetzen. So etwas wie JSON.parse(x[0]).ax ist kein Array von Objekten, sondern ein Array von Strings. Wenn Sie den Uniques b-Werte hinzufügen und die Reihenfolge von a / b umkehren, werden sie von Ihrer Funktion nicht mehr als eindeutig betrachtet. (zB "{" a ":" 1 "," b ": 2}"! = "{" b ": 2," a ":" 1 "} ") Vielleicht fehlt mir etwas, aber sollte das Ergebnis nicht wenigstens nützlich sein? Hier ist ein jsbin, um jsbin.com/utoruz/2/edit
Shanimal

1
Sie haben Recht, wenn Sie dieselben Schlüssel haben, aber in unterschiedlicher Reihenfolge die Implementierung unterbrechen. Ich bin mir jedoch nicht sicher, warum Sie den Schlüssel nur afür jedes Objekt überprüfen , wenn es möglicherweise doppelte Objekte gibt, die den Schlüssel nicht enthalten a. Es wäre jedoch sinnvoll, wenn es sich um aeine eindeutige ID handeln würde.
Larry Battle

Als ich die Frage beantwortete, schien es mir, dass der Fokus der Frage auf dem Überschreiben lag (a ==(=) b when a = b = {a:1}). Der Punkt meiner Antwort war der Iterator. Ich versuchte zu antworten, ohne mir Gedanken über das Motiv zu machen, das alles sein könnte, oder? (zB wollten sie vielleicht eine Liste von Marken, Farben aus einer Liste von Autos in einer Show extrahieren . jsbin.com/evodub/2/edit ) Prost!
Shanimal

Ich denke auch, dass es uns hilft, präzise Antworten zu geben, wenn jemand, der eine Frage stellt, ein Motiv liefert. Dies ist ein Rennen, deshalb bin ich lieber der Erste und kläre es bei Bedarf. Glücklicher St. Patricks Tag.
Shanimal

Nun, ich habe gerade eine weitere Gegenstimme abgegeben, weil dies meine Frage zum Vergleich verschachtelter Arrays beantwortet hat. iterator
Ich habe

15

Wenn ich eine Attribut-ID habe, ist dies meine bevorzugte Art im Unterstrich:

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}]

12

Die Verwendung der eindeutigen Unterstreichungsbibliothek funktioniert für mich. Ich mache die Liste basierend auf _id eindeutig und gebe dann den String-Wert von _id zurück:

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

10

Hier ist eine einfache Lösung, die einen tiefen Objektvergleich verwendet, um nach Duplikaten zu suchen (ohne auf die Konvertierung in JSON zurückzugreifen, was ineffizient und hackig ist).

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

Es filtert alle Elemente heraus, wenn sie später im Array ein Duplikat haben - so dass das letzte doppelte Element erhalten bleibt.

Das Testen auf eine doppelte Verwendung, _.isEqualdie einen optimierten Tiefenvergleich zwischen den beiden Objekten durchführt, finden Sie in der Dokumentation zum Unterstrich isEqual für weitere Informationen.

edit: aktualisiert, um _.filtereinen saubereren Ansatz zu verwenden


Kommt es nicht darauf an, eine vordefinierte einzigartige Eigenschaft zu haben? Ich mag das.
Don McCurdy

1
Eine gute Lösung für ein kleines Array von Objekten, aber eine Schleife innerhalb einer Schleife ist im Vergleich zur Bereitstellung einer eindeutigen ID teuer.
Penner


7

Versuchen Sie die Iteratorfunktion

Zum Beispiel können Sie das erste Element zurückgeben

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

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

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

})

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


Das Sekunden-Argument ist eigentlich optional, Sie können es auch tun_.uniq(x,function(i){ return i[0]; });
jakecraige

3

Hier ist meine Lösung (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

Beispiel:

_.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 ] ]

3

mit unterstrich musste ich string () in der iteratee funktion verwenden

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

0

Ich wollte diese einfache Lösung in einer einfachen Schreibweise lösen, mit ein wenig Rechenaufwand ... aber ist es nicht eine triviale Lösung mit einer minimalen Variablendefinition, oder?

function uniq(ArrayObjects){
  var out = []
  ArrayObjects.map(obj => {
    if(_.every(out, outobj => !_.isEqual(obj, outobj))) out.push(obj)
  })
  return out
}

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]; 
    }
);

Lassen Sie uns das aufschlüsseln. Zuerst können Sie die Array-Elemente nach ihrem String-Wert gruppieren

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

grouped sieht aus wie:

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

Dann holen wir uns das erste Element aus jeder Gruppe

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

bar sieht aus wie: [ { "a" : "1" }, { "b" : "2" } ]

Alles zusammen:

var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var bar = _.map(_.groupBy(foo, function (f) { 
        return JSON.stringify(f); 
    }), function (gr) { 
        return gr[0]; 
    }
);

3
Willkommen bei stackoverflow. Versuchen Sie zusätzlich zu dem von Ihnen bereitgestellten Code zu erklären, warum und wie dies das Problem behebt.
15.

Guter Anruf. Vielen Dank. Aktualisiert mit einer Aufschlüsselung, wie dies funktioniert.
Kelly Bigley

-5

Sie können es in Kurzform tun als:

_.uniq(foo, 'a')


Ihre Lösung funktioniert nicht für Arrays von Objekten, sondern nur für Arrays
Toucouleur
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.