Usuwanie zduplikowanych obiektów z podkreśleniem dla JavaScript


124

Mam taką tablicę:

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

Chciałbym to przefiltrować, aby mieć:

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

Próbowałem użyć _.uniq, ale myślę, że ponieważ { "a" : "1" }nie jest sobie równy, nie działa. Czy istnieje sposób na zapewnienie podkreślenia uniq z nadpisaną funkcją równa się?


Prześlij również swój kod
Chetter Hummin

Czy takie rzeczy { "a" : "2" }istnieją? Jeśli tak, to czy jest to atrybut czy wartość, która czyni go unikalnym?
Matt

Tak, mam atrybut jako klucz, zaimplementowałem indeks, który ktoś pokazał mi na inny temat, ale potem chciałem wyczyścić kod za pomocą niektórych popularnych bibliotek
plus

1
Proszę zmienić zaakceptowaną odpowiedź.
Vadorequest

Odpowiedzi:


232

.uniq / .unique akceptuje wywołanie zwrotne

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

Uwagi:

  1. Wartość zwracana przez wywołanie zwrotne używana do porównania
  2. Pierwszy obiekt porównania z unikalną wartością zwracaną używaną jako unikalna
  3. underscorejs.org nie wykazuje użycia wywołań zwrotnych
  4. lodash.com pokazuje użycie

Inny przykład: użycie wywołania zwrotnego do wyodrębnienia marek samochodów, kolorów z listy


falsenie jest wymagane dla _.uniq(). Również w lodash można to napisać w ten sposób _.uniq(a, 'a');, ponieważ zdziera on własność aobiektów.
Larry Battle

Skrót "'_.pluck' callback" działa tylko wtedy, gdy przekażesz wartość isSorted (np. _.uniq(a, false, 'a')) Pingowałem na github / bestiejs / lodash i powiedzieli, że problem został naprawiony na krawędzi. Więc jeśli nie używasz funkcji, upewnij się, że masz najnowszą. Może to nie stanowić problemu dla podkreślenia.
Shanimal

2
Iterator nie brzmi jak dobre imię, jest to funkcja podobna do skrótu, która będzie określać tożsamość każdego obiektu
Juan Mendes

Zmodyfikowano tak, aby używał callback, aby był bardziej spójny z dokumentami lodash :)
Shanimal

1
Na przykład w jsbin możesz mieć aktualizację. (1) Sprawia: _ (samochody) .uniq ('marka'). Map ('marka'). WartośćOf () AND (2) Kolory: _ (samochody) .uniq ('kolor'). Map ('kolor' ).wartość(). Możesz dojrzeć kolor i zamknąć. (Wszystko to, jeśli zaktualizujesz używane de lodash)
Vitor Tyburski

38

Jeśli chcesz usunąć duplikaty na podstawie identyfikatora, możesz zrobić coś takiego:

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

Zaakceptowana odpowiedź nie działa, gdy unikalnym identyfikatorem jest a Date. Jednak tak się dzieje.
gunwin

17

Realizacja odpowiedzi 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" } ]

przy okazji, jak zdobyłeś 4 głosy za? Aby uzyskać właściwości wyniku, musisz przywrócić każdą wartość tablicy z powrotem do obiektu. Coś w rodzaju, JSON.parse(x[0]).aponieważ x nie jest tablicą obiektów, jest to tablica ciągów. Ponadto, jeśli dodasz wartości b do unikatów i odwrócisz kolejność a / b, funkcja nie będzie już uważana za unikalną. (np. „{\" a \ ": \" 1 \ ", \" b \ ": 2}"! = "{\" b \ ": 2, \" a \ ": \" 1 \ "} „) Może czegoś mi brakuje, ale czy wynik nie powinien być przynajmniej przydatny? Oto jsbin ilustrujący jsbin.com/utoruz/2/edit
Shanimal

1
Masz rację, jeśli chodzi o posiadanie tych samych kluczy, ale w innej kolejności, przerywa implementację. Ale nie jestem pewien, dlaczego sprawdzasz tylko klucz adla każdego obiektu, podczas gdy mogą istnieć zduplikowane obiekty nie zawierające klucza a. Miałoby to jednak sens, gdyby abył to unikalny identyfikator.
Larry Battle

Kiedy odpowiadałem na pytanie, wydawało mi się, że celem pytania było unieważnienie (a ==(=) b when a = b = {a:1}). Punktem mojej odpowiedzi był iterator. Próbowałem odpowiedzieć, nie martwiąc się o motyw, który może być wszystkim, prawda? (np. może chcieli pobrać listę marek, kolorów z listy samochodów na wystawie. jsbin.com/evodub/2/edit ) Pozdrawiam!
Shanimal

Myślę też, że pomaga nam to w udzielaniu zwięzłych odpowiedzi, gdy ktoś zadający pytanie dostarcza motywacji. To jest wyścig, więc wolę być pierwszy i wyjaśniać, jeśli to konieczne. Wesołego dnia Świętego Patryka.
Shanimal

Cóż, po raz kolejny zagłosowałem za, ponieważ odpowiadało to na moje pytanie dotyczące porównywania zagnieżdżonych tablic. Szukałem tylko, jak zastąpićiterator
nevi_me

15

Kiedy mam identyfikator atrybutu, jest to mój preferowany sposób podkreślenia:

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

Użycie podkreślenia unikalnej biblioteki lib działa dla mnie, robię listę unikalną na podstawie _id, a następnie zwracam wartość ciągu _id:

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

10

Oto proste rozwiązanie, które wykorzystuje głębokie porównanie obiektów, aby sprawdzić, czy nie ma duplikatów (bez uciekania się do konwersji do formatu JSON, co jest nieefektywne i hakerskie)

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

Odfiltrowuje wszystkie elementy, jeśli mają one duplikat później w tablicy - tak, że ostatni zduplikowany element jest zachowywany.

Testowanie zduplikowanych zastosowań, _.isEqualktóre przeprowadza zoptymalizowane, głębokie porównanie między dwoma obiektami, zobacz dokumentację podkreślenia isEqual, aby uzyskać więcej informacji.

edycja: zaktualizowana do użycia, _.filterco jest bardziej przejrzystym podejściem


Nie zależy od posiadania predefiniowanej unikalnej właściwości? Lubię to.
Don McCurdy,

1
Dobre rozwiązanie dla małej tablicy obiektów, ale pętla w pętli jest kosztowna w porównaniu do podania unikalnego identyfikatora.
penner


7

Wypróbuj funkcję iteratora

Na przykład możesz zwrócić pierwszy element

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

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

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

})

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


argument sekund jest w rzeczywistości opcjonalny, możesz też zrobić_.uniq(x,function(i){ return i[0]; });
jakecraige

3

oto moje rozwiązanie (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

przykład:

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

z podkreśleniem musiałem użyć String () w funkcji iteratee

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

0

Chciałem rozwiązać to proste rozwiązanie w prosty sposób, z odrobiną bólu związanego z kosztami obliczeniowymi ... ale czy nie jest to trywialne rozwiązanie z minimalną definicją zmiennej, prawda?

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

Przełammy to. Najpierw pogrupuj elementy tablicy według ich wartości zadanych

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

grouped wygląda jak:

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

Następnie weźmy pierwszy element z każdej grupy

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

bar wygląda jak: [ { "a" : "1" }, { "b" : "2" } ]

Poskładać wszystko do kupy:

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
Witamy w stackoverflow. Oprócz podanego kodu spróbuj wyjaśnić, dlaczego i jak to rozwiązuje problem.
jtate

dobra decyzja. Dzięki. Zaktualizowano z opisem tego, jak to działa.
Kelly Bigley

-5

Możesz to zrobić w skrócie jako:

_.uniq(foo, 'a')


Twoje rozwiązanie nie działa dla tablic obiektów, ale tylko dla tablic
Toucouleur
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.