Rimozione di oggetti duplicati con trattino basso per Javascript


124

Ho questo tipo di array:

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

Mi piacerebbe filtrarlo per avere:

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

Ho provato a usare _.uniq, ma immagino che poiché { "a" : "1" }non è uguale a se stesso, non funziona. C'è un modo per fornire il carattere di sottolineatura uniq con una funzione uguale a sovrascrittura?


Pubblica anche il tuo codice
Chetter Hummin

Cose come { "a" : "2" }esistono? In caso affermativo è l'attributo o il valore che lo rende unico?
Matt

Sì, ho un attributo come chiave, ho implementato l'indice che qualcuno mi ha mostrato su un altro argomento, ma poi volevo pulire il mio codice usando alcune librerie comuni
inoltre-

1
Richiesta di modifica accettata risposta.
Vadorequest

Risposte:


232

.uniq / .unique accetta una richiamata

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

Appunti:

  1. Valore di ritorno della richiamata utilizzato per il confronto
  2. Primo oggetto di confronto con valore restituito univoco utilizzato come unico
  3. underscorejs.org non mostra l'utilizzo di callback
  4. lodash.com mostra l'utilizzo

Un altro esempio: utilizzo della richiamata per estrarre marche e colori di automobili da un elenco


falsenon è richiesto per _.uniq(). Anche in lodash avresti potuto scriverlo così _.uniq(a, 'a');, visto che coglierà la proprietà asugli oggetti.
Larry Battle

La "scorciatoia di callback '_.pluck'" funziona solo se si passa un valore per isSorted (ad esempio _.uniq(a, false, 'a')) Ho eseguito un ping su github / bestiejs / lodash e hanno detto che il problema è stato risolto al limite. Quindi, se non stai usando una funzione, assicurati di avere l'ultima. Questo potrebbe non essere un problema per il trattino basso.
Shanimal

2
Iterator non suona come un buon nome, è una funzione simile a un hash che determinerà l'identità di ogni oggetto
Juan Mendes

Modificato per utilizzare la richiamata per essere più coerente con i documenti lodash :)
Shanimal

1
Ad esempio su jsbin puoi avere un aggiornamento. (1) Rende: _ (cars) .uniq ('make'). Map ('make'). ValueOf () AND (2) Colori: _ (cars) .uniq ('color'). Map ('color' ).valore di(). Puoi maturare il colore e fare chiusure. (Tutto questo se aggiorni de lodash usato)
Vitor Tyburski

38

Se stai cercando di rimuovere i duplicati in base a un ID, potresti fare qualcosa del genere:

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

La risposta accettata non funziona quando l'identificatore univoco è un Date. Tuttavia questo fa.
gunwin

17

Attuazione della risposta di 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" } ]

a proposito, come hai ottenuto 4 voti positivi? Per ottenere le proprietà del risultato, è necessario ripristinare ogni valore di array in un oggetto. Qualcosa come JSON.parse(x[0]).aperché x non è un array di oggetti, è un array di stringhe. Inoltre, se aggiungi valori b agli unici e inverti l'ordine di a / b, questi non sono più considerati unici dalla tua funzione. (ad es. "" {\ "a \": \ "1 \", \ "b \": 2} "! =" {\ "b \": 2, \ "a \": \ "1 \"} ") Forse mi manca qualcosa, ma il risultato non dovrebbe almeno essere utile? Ecco un jsbin per illustrare jsbin.com/utoruz/2/edit
Shanimal

1
Hai ragione la condizione di avere le stesse chiavi ma in ordine diverso si interrompe l'implementazione. Ma non sono sicuro del motivo per cui stai controllando solo la chiave aper ogni oggetto, quando potrebbero esserci oggetti duplicati che non contengono la chiave a. Tuttavia, avrebbe senso se afosse un ID univoco.
Larry Battle

Quando stavo rispondendo alla domanda, mi è sembrato che l'obiettivo della domanda fosse quello di ignorare (a ==(=) b when a = b = {a:1}). Il punto della mia risposta è stato l'iteratore. Ho provato a rispondere senza preoccuparmi del motivo, che potrebbe essere qualsiasi cosa, giusto? (ad es. forse volevano estrarre un elenco di marche, colori da un elenco di auto in uno spettacolo. jsbin.com/evodub/2/edit ) Saluti!
Shanimal

Inoltre penso che ci aiuti a dare risposte concise quando qualcuno che fa una domanda fornisce il motivo. Questa è una gara, quindi preferisco essere il primo e chiarire se necessario. Buon giorno di San Patrizio.
Shanimal

Bene, ho appena dato un altro voto positivo perché questo ha risposto alla mia domanda sul confronto di array annidati. iterator
Stavo

15

Quando ho un ID attributo, questo è il mio modo preferito in underscore:

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

L'utilizzo del carattere di sottolineatura univoco lib che segue funziona per me, sto creando un elenco univoco sulla base di _id, quindi restituisco il valore String di _id:

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

10

Ecco una soluzione semplice, che utilizza un confronto approfondito degli oggetti per verificare la presenza di duplicati (senza ricorrere alla conversione in JSON, che è inefficiente e hacky)

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

Filtra tutti gli elementi se hanno un duplicato in un secondo momento nell'array, in modo che venga mantenuto l'ultimo elemento duplicato.

Il test per un duplicato utilizza _.isEqualche esegue un confronto approfondito ottimizzato tra i due oggetti, vedere la documentazione di sottolineatura isEqual per maggiori informazioni.

modifica: aggiornato per utilizzare _.filterche è un approccio più pulito


Non dipende dall'avere una proprietà unica predefinita? Mi piace.
Don McCurdy

1
Una buona soluzione per un piccolo array di oggetti, ma un ciclo all'interno di un ciclo è costoso rispetto alla fornitura di un ID uniq.
Penner


7

Prova la funzione iteratore

Ad esempio puoi restituire il primo elemento

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

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

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

})

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


l'argomento dei secondi è in realtà opzionale, puoi anche fare_.uniq(x,function(i){ return i[0]; });
jakecraige

3

ecco la mia soluzione (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

esempio:

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

con il carattere di sottolineatura ho dovuto usare String () nella funzione iteratee

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

0

Volevo risolvere questa semplice soluzione in un modo diretto di scrivere, con un po 'di dolore per le spese di calcolo ... ma non è una soluzione banale con una definizione minima di variabile, vero?

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

Analizziamo questo. Innanzitutto raggruppiamo gli elementi dell'array in base al loro valore in stringa

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

grouped sembra:

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

Quindi prendiamo il primo elemento da ogni gruppo

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

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

Metterli tutti insieme:

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
Benvenuto in stackoverflow. Oltre al codice che hai fornito, prova a dare qualche spiegazione sul perché e come questo risolve il problema.
jtate

Ottima scelta. Grazie. Aggiornato con una ripartizione di come funziona.
Kelly Bigley

-5

Puoi farlo in breve come:

_.uniq(foo, 'a')


la tua soluzione non funziona per gli array di oggetti, ma solo per gli array
Toucouleur
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.