Ottieni tutti i valori univoci in un array JavaScript (rimuovi duplicati)


1488

Ho una serie di numeri che devo accertarmi che siano univoci. Ho trovato lo snippet di codice qui sotto su Internet e funziona benissimo fino a quando l'array non contiene uno zero. Ho trovato questo altro script qui su Stack Overflow che sembra quasi esattamente simile, ma non fallisce.

Quindi, per aiutarmi ad imparare, qualcuno può aiutarmi a determinare dove lo script del prototipo sta andando storto?

Array.prototype.getUnique = function() {
 var o = {}, a = [], i, e;
 for (i = 0; e = this[i]; i++) {o[e] = 1};
 for (e in o) {a.push (e)};
 return a;
}

Altre risposte da una domanda duplicata:

Domanda simile:


3
@hippietrail Quella domanda più vecchia riguarda la ricerca e la restituzione dei soli duplicati (anch'io ero confuso!). La mia domanda è più sul perché questa funzione fallisce quando un array ha uno zero in essa.
Mottie,

Probabilmente vuoi rendere anche il titolo della domanda meno vago.
hippietrail,

Per i lettori futuri, quando si inizia a scoprire che è necessario modificare in modo algoritmico i contenuti della struttura dei dati in ogni momento, (ordinarli, rimuovere elementi ripetitivi, ecc.) O cercare elementi al suo interno ad ogni iterazione, è sicuro presumere che tu stai usando la struttura dei dati sbagliata in primo luogo e inizia a usarne una più appropriata per l'attività in corso (in questo caso un set di hash anziché un array).
Nurettin,

Ho copiato il codice da qualche altra parte, molto tempo fa ... ma sembra piuttosto semplice: o= object, a= array, i= indexe e= umm, qualcosa: P
Mottie

Risposte:


2661

Con JavaScript 1.6 / ECMAScript 5 è possibile utilizzare il filtermetodo nativo di un array nel modo seguente per ottenere un array con valori univoci:

function onlyUnique(value, index, self) { 
    return self.indexOf(value) === index;
}

// usage example:
var a = ['a', 1, 'a', 2, '1'];
var unique = a.filter( onlyUnique ); // returns ['a', 1, 2, '1']

Il metodo nativo filtereseguirà il ciclo nell'array e lascerà solo quelle voci che passano la data funzione di callback onlyUnique.

onlyUniquecontrolla, se il valore dato è il primo che si verifica. In caso contrario, deve essere un duplicato e non verrà copiato.

Questa soluzione funziona senza alcuna libreria aggiuntiva come jQuery o prototype.js.

Funziona anche con array con tipi di valore misti.

Per i vecchi browser (<ie9), che non supportano i metodi nativi filtere indexOfpuoi trovare soluzioni nella documentazione MDN per filter e indexOf .

Se si desidera mantenere l'ultima occorrenza di un valore, sostituire semplicemente indexOfcon lastIndexOf.

Con ES6 si potrebbe abbreviare a questo:

// usage example:
var myArray = ['a', 1, 'a', 2, '1'];
var unique = myArray.filter((v, i, a) => a.indexOf(v) === i); 

// unique is ['a', 1, 2, '1']

Grazie a Camilo Martin per il suggerimento nel commento.

ES6 ha un oggetto nativo Setper memorizzare valori univoci. Per ottenere un array con valori univoci, puoi fare ora questo:

var myArray = ['a', 1, 'a', 2, '1'];

let unique = [...new Set(myArray)]; 

// unique is ['a', 1, 2, '1']

Il costruttore di Setprende un oggetto iterabile, come Array, e l'operatore spread ...trasforma il set in un array. Grazie a Lukas Liese per il suggerimento nel commento.


55
Sfortunatamente, questa soluzione funzionerà molto più lentamente. Stai eseguendo il looping due volte, una con filtro e una con indice di
Jack Franzen il

19
@JackFranzen Più lento di cosa? La soluzione di Rafael ? La soluzione Rafaels non funziona per array di tipo misto. Per il mio esempio ['a', 1, 'a', 2, '1']otterresti ['a', 1, 2]. Ma questo non è quello che mi aspettavo. A proposito, molto più lento è molto relativo.
TLindig,

25
Nel moderno JS: .filter((v,i,a)=>a.indexOf(v)==i)(notazione con freccia grassa).
Camilo Martin,

164
let unique_values = [...new Set(random_array)]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Lukas Liesis

5
Per una risposta molto più dettagliate, tra cui molte possibilità - come l'ordinamento prima, e si occupano di diversi tipi di dati - vedi stackoverflow.com/a/9229821/368896
Dan Nissenbaum

876

Risposta aggiornata per ES6 / ES2015 : utilizzando il set , la soluzione a linea singola è:

var items = [4,5,4,6,3,4,5,2,23,1,4,4,4]
var uniqueItems = Array.from(new Set(items))

Che ritorna

[4, 5, 6, 3, 2, 23, 1]

Come suggerito da le_m , questo può anche essere abbreviato usando l' operatore spread , come

var uniqueItems = [...new Set(items)]

11
Si noti che l'array interno non funzionerebbeArray.from(new Set([[1,2],[1,2],[1,2,3]]))
Alexander Goncharov,

3
Prestazioni di questa soluzione rispetto a myArray.filter((v, i, a) => a.indexOf(v) === i);?
Simoyw,

43
Nota che se usi Sete aggiungi oggetti invece di valori primitivi conterrà riferimenti univoci agli oggetti. Quindi l'insieme sin let s = new Set([{Foo:"Bar"}, {Foo:"Bar"}]);restituirà questo: Set { { Foo: 'Bar' }, { Foo: 'Bar' } }che è una Setcon riferimenti a oggetti unici per oggetti che contengono gli stessi valori. Se scrivi let o = {Foo:"Bar"};e quindi crei un set con due riferimenti in questo modo let s2 = new Set([o,o]);Set { { Foo: 'Bar' } }
:,

2
Entrambe queste opzioni presentano problemi su IE10 anche con uno strato polyfill.io presente
Thymine,

2
nuovo test case jsperf.com/array-filter-unique-vs-new-set/1 sembra new Setil trofeo
shuk

167

Mi rendo conto che questa domanda ha già più di 30 risposte. Ma ho prima letto tutte le risposte esistenti e ho fatto le mie ricerche.

Ho diviso tutte le risposte a 4 possibili soluzioni:

  1. Usa la nuova funzione ES6: [...new Set( [1, 1, 2] )];
  2. Usa l'oggetto { }per prevenire i duplicati
  3. Usa array di helper [ ]
  4. Uso filter + indexOf

Ecco i codici di esempio trovati nelle risposte:

Usa la nuova funzione ES6: [...new Set( [1, 1, 2] )];

function uniqueArray0(array) {
  var result = Array.from(new Set(array));
  return result    
}

Usa l'oggetto { }per prevenire i duplicati

function uniqueArray1( ar ) {
  var j = {};

  ar.forEach( function(v) {
    j[v+ '::' + typeof v] = v;
  });

  return Object.keys(j).map(function(v){
    return j[v];
  });
} 

Usa array di helper [ ]

function uniqueArray2(arr) {
    var a = [];
    for (var i=0, l=arr.length; i<l; i++)
        if (a.indexOf(arr[i]) === -1 && arr[i] !== '')
            a.push(arr[i]);
    return a;
}

Uso filter + indexOf

function uniqueArray3(a) {
  function onlyUnique(value, index, self) { 
      return self.indexOf(value) === index;
  }

  // usage
  var unique = a.filter( onlyUnique ); // returns ['a', 1, 2, '1']

  return unique;
}

E mi chiedevo quale fosse più veloce. Ho realizzato un esempio di foglio di Google per testare le funzioni. Nota: ECMA 6 non è disponibile in Fogli Google, quindi non posso provarlo.

Ecco il risultato dei test: inserisci qui la descrizione dell'immagine

Mi aspettavo di vedere che il codice usando l'oggetto { }vincerà perché usa l'hash. Quindi sono contento che i test abbiano mostrato i migliori risultati per questo algoritmo in Chrome e IE. Grazie a @rab per il codice .


L'opzione "filter + indexOf" è estremamente lenta su array con oltre 100.000 elementi. Ho dovuto usare l'approccio "mappa dell'oggetto", tuttavia si rompe l'ordinamento originale.
liberborn,

Il numero più alto è più lento, non più veloce?
fletchsod,

3
@ fletchsod, i numeri sono il tempo in ms per eseguire il codice.
Max Makhrov,

1
I numeri sono wong! Lo script di app di Google viene eseguito sul loro server, non sul browser del client, pertanto le prestazioni su Chrome / IE (o praticamente su qualsiasi browser) saranno le stesse!
Jay Dadhania,

1
Mentre è possibile eseguire JS sul lato client da Google Script (tramite google.script.host o google.script.run - non sono sicuro di quale), la risposta afferma chiaramente che "ECMA 6 non è disponibile in Fogli Google" - pertanto possiamo tranquillamente supporre che il poster abbia utilizzato Google Script lato server per questo - e quindi la risposta mostra le prestazioni del Google Server, non del browser !!
Jay Dadhania,

134

Puoi anche usare underscore.js .

console.log(_.uniq([1, 2, 1, 3, 1, 4]));
<script src="http://underscorejs.org/underscore-min.js"></script>

che restituirà:

[1, 2, 3, 4]

22
Per favore, faccia questo. Non collegare qualcosa al prototipo di array. Per favore.
Jacob Dalton,

5
@JacobDalton - Questo non estende il prototipo dell'array. È spaziato nell'oggetto _.
superluminario,

25
@superluminary So che è per questo che ho detto, per favore, fallo . La soluzione accettata suggerisce di modificare il prototipo dell'array. NON farlo.
Jacob Dalton,

21
@JacobDalton Per favore, non farlo. Non è necessario aggiungere una libreria aggiuntiva solo per un piccolo lavoro che può essere fatto conarray = [...new Set(array)]

3
Ciò ha vanificato lo scopo dell'utilizzo delle funzionalità ES e l'aggiunta di più librerie non fa che gonfiare di più il sito Web.
fletchsod,

68

One Liner, JavaScript puro

Con sintassi ES6

list = list.filter((x, i, a) => a.indexOf(x) == i)

x --> item in array
i --> index of item
a --> array reference, (in this case "list")

inserisci qui la descrizione dell'immagine

Con sintassi ES5

list = list.filter(function (x, i, a) { 
    return a.indexOf(x) == i; 
});

Compatibilità del browser : IE9 +


14
@Spets Ha un costo quadratico, quindi non è la risposta migliore
Oriol

@Spet come tutti gli unici / distinti ... sì, sii intelligente, usa radix per ordinare prima, nella maggior parte dei casi è più lento di 0 (n2) ricerca veloce.
Doker,

grazie ragazzi! Non me ne sono reso conto quando ho visto il codice per la prima volta, ma ora è un po 'più ovvio.
Spets

51

Da allora ho trovato un bel metodo che utilizza jQuery

arr = $.grep(arr, function(v, k){
    return $.inArray(v ,arr) === k;
});

Nota: questo codice è stato estratto dal post di punzonatura delle anatre di Paul Irish - Ho dimenticato di dare credito: P


8
Una soluzione concisa, ma chiamare inArray è molto meno efficiente di chiamare hasOwnProperty.
Mister Smith,

Anche questo è O (N ^ 2), giusto? Considerando che il dizionario o l'approccio hasOwnProperty sarebbe probabilmente O (N * logN).
speedplane il

Questo ha funzionato per me, tutte le altre soluzioni non erano supportate da Internet Explorer.
Kerl,

51

La soluzione più breve con ES6: [...new Set( [1, 1, 2] )];

O se si desidera modificare il prototipo dell'array (come nella domanda originale):

Array.prototype.getUnique = function() {
    return [...new Set( [this] )];
};

EcmaScript 6 è attualmente solo parzialmente implementato nei browser moderni (agosto 2015), ma Babel è diventato molto popolare per la traduzione di ES6 (e persino ES7) su ES5. In questo modo puoi scrivere il codice ES6 oggi!

Se ti stai chiedendo cosa ...significhi, si chiama operatore di diffusione . Da MDN : «L'operatore spread consente di espandere un'espressione in punti in cui sono previsti più argomenti (per chiamate di funzione) o più elementi (per valori letterali di array)». Poiché un set è iterabile (e può avere solo valori univoci), l'operatore spread espande il set per riempire l'array.

Risorse per l'apprendimento dell'ES6:


3
puoi farlo ancora più breve, con a = [...Set(a)], ma, comunque, questo è solo Firefox, per ora.
c69,

@ c69, giusto, non sarà più breve di così. Anche gli utenti di SpiderMonkey apprezzeranno.
Torsten Becker,

Funziona con Babele. Devi solo includere Array.da Shim anche:require ( "core-js/fn/array/from" );
Vladislav Rastrusny il

dovrebbe essere Array.prototype.getUnique = function () {return [... new Set ([this])]; }; oppure, Array.prototype.getUnique = function () {return [... new Set (this)]; }; L'ho applicato sul seguente $ ('body'). Html (). ToLowerCase (). Match (/ ([a-zA-Z0-9 ._ + -] + @ [a-zA-Z0-9. . _-] + \ [a-zA-Z0-9 ._-] +) / gi) .getUnique (); Il primo mi ha restituito un valore univoco solo in cui il secondo ha risintonizzato tutti i duplicati rimossi.
ashique,

[...Set(['a', 1, 'a', 2, '1'])]lancerà un TypeError, quindi è ancora saggio conservare new:[...new Set(['a', 1, 'a', 2, '1'])]
Adam Katz l'

36

La soluzione più semplice:

var arr = [1, 3, 4, 1, 2, 1, 3, 3, 4, 1];
console.log([...new Set(arr)]);

O:

var arr = [1, 3, 4, 1, 2, 1, 3, 3, 4, 1];
console.log(Array.from(new Set(arr)));


4
Si noti che questo non è supportato su IE10 e versioni precedenti. Nota che IE11 + e Safari offrono un supporto limitato (nemmeno sicuro che l'esempio sopra
funzioni

32

Il modo più semplice e veloce (in Chrome) per farlo:

Array.prototype.unique = function() {
    var a = [];
    for (var i=0, l=this.length; i<l; i++)
        if (a.indexOf(this[i]) === -1)
            a.push(this[i]);
    return a;
}

Esamina semplicemente ogni elemento dell'array, verifica se quell'elemento è già nell'elenco e, in caso contrario, passa all'array che viene restituito.

Secondo jsPerf, questa funzione è la più veloce di quelle che ho trovato ovunque : sentiti libero di aggiungere il tuo però.

La versione non prototipo:

function uniques(arr) {
    var a = [];
    for (var i=0, l=arr.length; i<l; i++)
        if (a.indexOf(arr[i]) === -1 && arr[i] !== '')
            a.push(arr[i]);
    return a;
}

Ordinamento

Quando è inoltre necessario ordinare l'array, il seguente è il più veloce:

Array.prototype.sortUnique = function() {
    this.sort();
    var last_i;
    for (var i=0;i<this.length;i++)
        if ((last_i = this.lastIndexOf(this[i])) !== i)
            this.splice(i+1, last_i-i);
    return this;
}

o non prototipo:

function sortUnique(arr) {
    arr.sort();
    var last_i;
    for (var i=0;i<arr.length;i++)
        if ((last_i = arr.lastIndexOf(arr[i])) !== i)
            arr.splice(i+1, last_i-i);
    return arr;
}

Questo è anche più veloce del metodo sopra nella maggior parte dei browser non Chrome.


Su Linux, Chrome 55.0.2883 preferisce arr.unique () e arrclone2.sortFilter () di swilliams è più lento (78% più lento). Tuttavia, Firefox 51.0.0 (con molti addon) ha swilliams il più veloce (ma ancora più lento di Ops / sec rispetto a qualsiasi altro risultato di Chrome) con jQuery $ .grep di mottie (arr, jqFilter) più lento (46% più lento). Il tuo arr.uniq () è stato più lento del 30%. Ho eseguito ogni test due volte e ho ottenuto risultati coerenti. L' arr.getUnique () di Rafael ha ottenuto il secondo posto in entrambi i browser.
Adam Katz,

jsPerf al momento è difettoso , quindi la mia modifica a questo test non ha eseguito il commit di tutto, ma ha comportato l'aggiunta di due test: Cocco's toUnique () batte ES6 list.filter () di Vamsi su entrambi i browser, battendo sortFilter di swilliams () per # 1 su FF (sortFilter era più lento del 16%) e battendo i test ordinati (che era più lento del 2%) per # 3 su Chrome.
Adam Katz,

Ah, non avevo notato che quei test erano banalmente piccoli e non contano davvero. Un commento alla risposta accettata descrive questo problema e offre una correzione in una revisione del test, in cui il codice Rafael è facilmente il più veloce e il codice arr.unique di Joetje50 è più lento del 98%. Ho anche fatto un'altra revisione, come indicato in questo commento .
Adam Katz,

1
Bene, in realtà l'algoritmo implementato nella uniquefunzione ha una complessità O (n ^ 2) mentre quello in getUniqueè O (n). Il primo potrebbe essere più veloce su piccoli set di dati, ma come puoi discutere con la matematica :) Puoi assicurarti che quest'ultimo sia più veloce se lo esegui su una serie di, diciamo, 1e5 elementi unici
Mikhail Dudin

31

SOLO PERFORMANCE! questo codice è probabilmente 10 volte più veloce di tutti i codici qui * funziona su tutti i browser e ha anche il minor impatto di memoria .... e altro

se non hai bisogno di riutilizzare il vecchio array; btw fai le altre operazioni necessarie prima di convertirlo in unico qui è probabilmente il modo più veloce per farlo, anche molto breve.

var array=[1,2,3,4,5,6,7,8,9,0,1,2,1];

allora puoi provare questo

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 1];

function toUnique(a, b, c) { //array,placeholder,placeholder
  b = a.length;
  while (c = --b)
    while (c--) a[b] !== a[c] || a.splice(c, 1);
  return a // not needed ;)
}
console.log(toUnique(array));
//[3, 4, 5, 6, 7, 8, 9, 0, 2, 1]

Mi è venuta in mente questa funzione leggendo questo articolo ...

http://www.shamasis.net/2009/09/fast-algorithm-to-find-unique-items-in-javascript-array/

Non mi piace il ciclo for. ha molti parametri. Mi piace il ciclo while--. mentre è il ciclo più veloce in tutti i browser tranne quello che a tutti noi piace così tanto ... Chrome.

comunque ho scritto la prima funzione che usa while. E sì, è un po 'più veloce della funzione trovata nell'articolo, ma non abbastanza.unique2()

il prossimo passo usa js moderni. Object.keys ho sostituito l'altro per loop con Object.keys di js1.7 ... un po 'più veloce e più corto (in chrome 2x più veloce);). Non abbastanza!. unique3().

a questo punto stavo pensando a ciò di cui ho veramente bisogno nella MIA funzione unica. non ho bisogno del vecchio array, voglio una funzione veloce. così ho usato 2 mentre loops + splice.unique4()

Inutile dire che sono rimasto colpito.

cromo: le solite 150.000 operazioni al secondo sono balzate a 1.800.000 operazioni al secondo.

vale a dire: 80.000 op / s contro 3.500.000 op / s

iOS: 18.000 op / s vs 170.000 op / s

safari: 80.000 op / s contro 6.000.000 op / s

Prova http://jsperf.com/wgu o meglio usa console.time ... microtime ... qualunque cosa

unique5() è solo per mostrarti cosa succede se vuoi mantenere il vecchio array.

Non usare Array.prototypese non sai cosa stai facendo. ho appena fatto un sacco di copia e passato. Utilizzare Object.defineProperty(Array.prototype,...,writable:false,enumerable:false})se si desidera creare un prototipo nativo.esempio : https://stackoverflow.com/a/20463021/2450730

Demo http://jsfiddle.net/46S7g/

NOTA: il tuo vecchio array viene distrutto / perché diventa unico dopo questa operazione.

se non riesci a leggere il codice sopra chiedi, leggi un libro javascript o qui ci sono alcune spiegazioni sul codice più breve. https://stackoverflow.com/a/21353032/2450730

alcuni stanno usando indexOf ... non ... http://jsperf.com/dgfgghfghfghghgfhgfhfghfhgfh

per array vuoti

!array.length||toUnique(array); 

4
testato su node.js, con un array di 100k di URL (stringhe). Il risultato è stato 2 volte più lento di underscore.js _.uniq ... sebbene un jsperf separato sia d'accordo con te ( jsperf.com/uniq-performance/5 ), sono deluso :(
xShirase

3
non stai testando correttamente in jsperf ... nel tuo esempio definisci la funzione ogni volta ... ma le funzioni underscore.js sono già definite .. questo penalizza la mia funzione. test 3 e 4. anche un'altra cosa che dovrei dire è che se usi variabili miste (stringhe e numeri) devi sostituire a [b]! == a [c] con a [b]! = a [c]
cocco

2
Non sono sicuro che jsPerf abbia corretto, ma sembra che l'alternativa Riduci (che, per me è più facile da capire) sia un po 'più veloce del 94% rispetto alla tua soluzione. jsperf.com/reduce-for-distinct Modifica: il tuo è più lento su Chrome, lo stesso su Edge e più veloce su Firefox.
Victor Ivens

3
Utilizzabile solo con matrici piccole / bassa percentuale di duplicati . Ogni volta che viene trovato un oggetto non unico, il motore sarà costretto a spostare tutti gli altri elementi a sinistra: 1) ripetendo su di essi 2) leggendoli 3) scrivendoli in una nuova posizione. E poi, crea anche un nuovo array con quell'elemento impiombato e lo restituisce come risultato ... L'algoritmo si degrada rapidamente con array più grandi / duplicati più frequenti. Per array di dimensioni 1000-> 10000-> 50000 e ~ 40% duplicati, il tempo medio impiegato sarà di 2-> 775-> 19113 ms. (la dimensione cambia come 1-> x10-> x5, l'ora come 1-> x10-> x25) in Chrome.
ankhzet,

1
Grazie. Bel approccio. Ma per quanto riguarda l'ordine degli articoli corretti?
liberborn,

28

Molte delle risposte qui potrebbero non essere utili ai principianti. Se il de-duping di un array è difficile, conosceranno davvero la catena di prototipi o persino jQuery?

Nei browser moderni, una soluzione semplice e pulita consiste nell'archiviare i dati in un set , progettato per essere un elenco di valori univoci.

const cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];
const uniqueCars = Array.from(new Set(cars));

Il Array.from è utile per convertire il set di nuovo a una matrice in modo da avere un facile accesso a tutti i metodi impressionante (caratteristiche) che gli array hanno. Ci sono anche altri modi per fare la stessa cosa. Ma potresti non averne bisogno Array.from, in quanto i set hanno molte funzioni utili come forOach .

Se è necessario supportare Internet Explorer precedente e quindi non è possibile utilizzare Set, una tecnica semplice consiste nel copiare gli elementi in un nuovo array verificando in anticipo se sono già nel nuovo array.

// Create a list of cars, with duplicates.
var cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];
// Create a list of unique cars, to put a car in if we haven't already.
var uniqueCars = [];

// Go through each car, one at a time.
cars.forEach(function (car) {
    // The code within the following block runs only if the
    // current car does NOT exist in the uniqueCars list
    // - a.k.a. prevent duplicates
    if (uniqueCars.indexOf(car) === -1) {
        // Since we now know we haven't seen this car before,
        // copy it to the end of the uniqueCars list.
        uniqueCars.push(car);
    }
});

Per renderlo immediatamente riutilizzabile, mettiamolo in una funzione.

function deduplicate(data) {
    if (data.length > 0) {
        var result = [];

        data.forEach(function (elem) {
            if (result.indexOf(elem) === -1) {
                result.push(elem);
            }
        });

        return result;
    }
}

Quindi, per sbarazzarci dei duplicati, ora faremmo questo.

var uniqueCars = deduplicate(cars);

La deduplicate(cars)parte diventa l'elemento che abbiamo chiamato risultato al termine della funzione.

Basta passare il nome di qualsiasi array che ti piace.


A proposito, ho usato un array pieno di stringhe per dimostrare che la mia tecnica è flessibile. Funzionerà correttamente per i numeri.
Seth Holladay,

20
["Defects", "Total", "Days", "City", "Defects"].reduce(function(prev, cur) {
  return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev;
 }, []);

[0,1,2,0,3,2,1,5].reduce(function(prev, cur) {
  return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev;
 }, []);

Puoi spiegare perché non hai semplicemente pushl'elemento sull'array invece di usarlo concat? Ho provato a utilizzare push e non è riuscito. Sto cercando una spiegazione.
makenova,

1
Il motivo è nel valore restituito. concat restituisce l'array modificato (esattamente ciò che deve essere restituito all'interno della funzione di riduzione), mentre push restituisce un indice a cui è possibile accedere al valore push. Questo risponde alla tua domanda?
sergeyz,

Apparentemente, questa soluzione è abbastanza veloce in Chrome (e nodo). jsperf.com/reduce-for-distinct Questa è la versione ES6: [0,1,2,0,3,2,1,5].reduce((prev, cur) => ~prev.indexOf(cur) ? prev : prev.concat([cur]), []);
Victor Ivens,

nota a NaNmargine : questa implementazione non è amichevole
Alireza,

19

Possiamo farlo usando i set ES6:

var duplicatedArray = [1, 2, 3, 4, 5, 1, 1, 1, 2, 3, 4];
var uniqueArray = Array.from(new Set(duplicatedArray));

console.log(uniqueArray);

// L'output sarà

uniqueArray = [1,2,3,4,5];

17

Questo prototipo getUniquenon è del tutto corretto, perché se ho un array come: ["1",1,2,3,4,1,"foo"]tornerà ["1","2","3","4"]ed "1"è una stringa ed 1è un numero intero; sono diversi.

Ecco una soluzione corretta:

Array.prototype.unique = function(a){
    return function(){ return this.filter(a) }
}(function(a,b,c){ return c.indexOf(a,b+1) < 0 });

utilizzando:

var foo;
foo = ["1",1,2,3,4,1,"foo"];
foo.unique();

Quanto sopra produrrà ["1",2,3,4,1,"foo"].


1
Si noti che $foo = 'bar'è il modo PHP di dichiarare le variabili. Funzionerà in JavaScript, ma creerà un globale implicito e generalmente non dovrebbe essere fatto.
Camilo Martin,

@CamiloMartin scusa ma ti sbagli, $ foo è globale perché l'esempio non è chiuso e manca la parola chiave var. Niente a che vedere con il dollaro jsfiddle.net/robaldred/L2MRb
Rob

8
@Rob è esattamente quello che sto dicendo, la gente di PHP penserà che $foosia il modo di dichiarare variabili in javascript mentre in realtàvar foo è.
Camilo Martin,

13

Senza estendere Array.prototype (si dice che sia una cattiva pratica) o usare jquery / underscore, puoi semplicemente filterl'array.

Mantenendo l'ultima occorrenza:

    function arrayLastUnique(array) {
        return array.filter(function (a, b, c) {
            // keeps last occurrence
            return c.indexOf(a, b + 1) < 0;
        });
    },

o prima occorrenza:

    function arrayFirstUnique(array) {
        return array.filter(function (a, b, c) {
            // keeps first occurrence
            return c.indexOf(a) === b;
        });
    },

Bene, è solo javascript ECMAScript 5+, che significa solo IE9 +, ma è bello per uno sviluppo in HTML / JS nativo (app di Windows Store, Firefox OS, Sencha, Phonegap, Titanium, ...).


2
Il fatto che sia js 1.6 non significa che non puoi usarlo filter. Nella pagina MDN hanno un'implementazione per Internet Explorer, intendo, i browser più vecchi. Inoltre: JS 1.6 si riferisce solo al motore js di Firefox, ma la cosa giusta da dire è che è ECMAScript 5.
Camilo Martin,

@CamiloMartin Ho cambiato 1.6 in ECMAScript5. Grazie.
Cœur

13

Magia

a.filter(e=>!(t[e]=e in t)) 

O (n) prestazione ; supponiamo che il tuo array sia in ae t={}. Spiegazione qui (+ Jeppe impr.)


14
questo aspetto è così fantastico, che senza una solida spiegazione sono caduto mi farai i miei bitcoin quando
eseguo

3
quello che volevo dire è che dovresti espandere la tua risposta con alcune spiegazioni e commenti decostruiti. non aspettarti che le persone troveranno risposte utili come questa. (anche se sembra davvero bello e probabilmente funziona)
Ondřej Želazko

1
@Jeppe quando eseguo il tuo miglioramento, allora provo un effetto (prima di non sapere che posso usare l' inoperatore al di fuori dell'altra costruzione rispetto al forloop: P) - Grazie - Lo apprezzo e darò +2 alle altre tue buone risposte .
Kamil Kiełczewski

2
questo non crea una variabile globale tche rimane viva dopo il filtraggio ... ??
philipp

1
Questa è davvero una soluzione complicata. Sfortunatamente, devi dichiarare fuori dall'espressione a meno che tu non voglia definire una variabile globale e / o fare affidamento sul tuo bundle di moduli per nascondere il globale. Sembra elegante, ma fai un favore a te stesso (e al tuo team di sviluppo) e scrivi solo due righe: let t = {}; a.filter (e =>! (t [e] = e in t))
ford04

13
[...new Set(duplicates)]

Questo è il più semplice e referenziato da MDN Web Docs .

const numbers = [2,3,4,4,2,3,3,4,4,5,5,6,6,7,5,32,3,4,5]
console.log([...new Set(numbers)]) // [2, 3, 4, 5, 6, 7, 32]

Sebbene questo codice possa risolvere la domanda, inclusa una spiegazione di come e perché questo risolva il problema, aiuterebbe davvero a migliorare la qualità del tuo post e probabilmente si tradurrà in più voti positivi. Ricorda che stai rispondendo alla domanda per i lettori in futuro, non solo per la persona che chiede ora. Modifica la tua risposta per aggiungere spiegazioni e fornire un'indicazione di quali limitazioni e ipotesi si applicano.
id.ot

11

Se si utilizza il framework Prototype non è necessario eseguire i cicli 'for', è possibile utilizzare http://www.prototypejs.org/api/array/uniq in questo modo:

var a = Array.uniq();  

Che produrrà un array duplicato senza duplicati. Mi sono imbattuto nella tua domanda cercando un metodo per contare record di array distinti così dopo

uniq ()

ero solito

dimensione()

e c'era il mio semplice risultato. ps Scusami se ho sbagliato a scrivere qualcosa

modifica: se si desidera sfuggire ai record non definiti, è possibile che si desideri aggiungere

compatto()

prima, in questo modo:

var a = Array.compact().uniq();  

14
perché ho trovato una risposta migliore, penso che gli argomenti siano per tutte le persone, non solo per quello che ha chiesto
Decebal,

11

Ora usando i set è possibile rimuovere i duplicati e convertirli nuovamente nella matrice.

var names = ["Mike","Matt","Nancy", "Matt","Adam","Jenny","Nancy","Carl"];

console.log([...new Set(names)])

Un'altra soluzione è utilizzare l'ordinamento e il filtro

var names = ["Mike","Matt","Nancy", "Matt","Adam","Jenny","Nancy","Carl"];
var namesSorted = names.sort();
const result = namesSorted.filter((e, i) => namesSorted[i] != namesSorted[i+1]);
console.log(result);


10

Questo perché 0è un valore falso in JavaScript.

this[i] sarà falso se il valore della matrice è 0 o qualsiasi altro valore di falsa.


Ahhhh, ok vedo ora ... ma ci sarebbe una soluzione semplice per farlo funzionare?
Mottie,

10
Array.prototype.getUnique = function() {
    var o = {}, a = []
    for (var i = 0; i < this.length; i++) o[this[i]] = 1
    for (var e in o) a.push(e)
    return a
}

Penso che questo non funzionerà se l'array contiene oggetti / array e non sono sicuro che manterrà il tipo di scalari.
Camilo Martin,

Sì, tutto viene stretto. Ciò potrebbe essere risolto memorizzando il valore originale oinvece di solo a 1, sebbene il confronto di uguaglianza sarebbe comunque rigorosamente (sebbene, al di là di tutte le possibili uguaglianze di Javascript, non sembra troppo irragionevole).
effimero

Array.prototype potrebbe essere esteso solo con metodi non enumerabili .... Object.defineProperty (Array.prototype, "getUnique", {}) ... ma l'idea di usare un oggetto helper è molto bella
bortunac

10

Ho avuto un problema leggermente diverso in cui avevo bisogno di rimuovere oggetti con proprietà ID duplicate da un array. questo ha funzionato.

let objArr = [{
  id: '123'
}, {
  id: '123'
}, {
  id: '456'
}];

objArr = objArr.reduce((acc, cur) => [
  ...acc.filter((obj) => obj.id !== cur.id), cur
], []);

console.log(objArr);


9

La risposta più semplice è:

const array = [1, 1, 2, 2, 3, 5, 5, 2];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // [1, 2, 3, 5]

Semplice ma non funziona con una matrice di oggetti.
Thanwa Ch.

7

Non sono sicuro del motivo per cui Gabriel Silveira abbia scritto la funzione in quel modo, ma una forma più semplice che funziona altrettanto bene per me e senza la minimizzazione è:

Array.prototype.unique = function() {
  return this.filter(function(value, index, array) {
    return array.indexOf(value, index + 1) < 0;
  });
};

o in CoffeeScript:

Array.prototype.unique = ->
  this.filter( (value, index, array) ->
    array.indexOf(value, index + 1) < 0
  )

7

Se stai bene con dipendenze extra, o hai già una delle librerie nella tua base di codice, puoi rimuovere i duplicati da un array sul posto usando LoDash (o Underscore).

uso

Se non lo hai già nel tuo codebase, installalo usando npm:

npm install lodash

Quindi usalo come segue:

import _ from 'lodash';
let idArray = _.uniq ([
    1,
    2,
    3,
    3,
    3
]);
console.dir(idArray);

Su:

[ 1, 2, 3 ]

7

Ciò ha ricevuto molte risposte, ma non ha soddisfatto il mio bisogno particolare.

Molte risposte sono così:

a.filter((item, pos, self) => self.indexOf(item) === pos);

Ma questo non funziona per le matrici di oggetti complessi.

Supponiamo di avere un array come questo:

const a = [
 { age: 4, name: 'fluffy' },
 { age: 5, name: 'spot' },
 { age: 2, name: 'fluffy' },
 { age: 3, name: 'toby' },
];

Se vogliamo gli oggetti con nomi univoci, dovremmo usare array.prototype.findIndexinvece di array.prototype.indexOf:

a.filter((item, pos, self) => self.findIndex(v => v.name === item.name) === pos);

Ottima soluzione, attenzione che un nuovo array tornerà da una funzione. (non si modifica da solo)
Thanwa Ch.

6

Da Shamasis Bhattacharya blog 's (O (2n) Tempo di complessità):

Array.prototype.unique = function() {
    var o = {}, i, l = this.length, r = [];
    for(i=0; i<l;i+=1) o[this[i]] = this[i];
    for(i in o) r.push(o[i]);
    return r;
};

Dal blog di Paul Irish : miglioramento su JQuery .unique():

(function($){

    var _old = $.unique;

    $.unique = function(arr){

        // do the default behavior only if we got an array of elements
        if (!!arr[0].nodeType){
            return _old.apply(this,arguments);
        } else {
            // reduce the array to contain no dupes via grep/inArray
            return $.grep(arr,function(v,k){
                return $.inArray(v,arr) === k;
            });
        }
    };
})(jQuery);

// in use..
var arr = ['first',7,true,2,7,true,'last','last'];
$.unique(arr); // ["first", 7, true, 2, "last"]

var arr = [1,2,3,4,5,4,3,2,1];
$.unique(arr); // [1, 2, 3, 4, 5]

6

Trovare valori di array univoci in un metodo semplice

function arrUnique(a){
  var t = [];
  for(var x = 0; x < a.length; x++){
    if(t.indexOf(a[x]) == -1)t.push(a[x]);
  }
  return t;
}
arrUnique([1,4,2,7,1,5,9,2,4,7,2]) // [1, 4, 2, 7, 5, 9]

6

Sembra che abbiamo perso la risposta di Rafael , che è stata la risposta accettata per alcuni anni. Questa è stata (almeno nel 2017) la soluzione più performante se non si dispone di un array di tipo misto :

Array.prototype.getUnique = function(){
    var u = {}, a = [];
    for (var i = 0, l = this.length; i < l; ++i) {
        if (u.hasOwnProperty(this[i])) {
            continue;
        }
        a.push(this[i]);
        u[this[i]] = 1;
    }
return a;
}

Se fare avere una matrice di tipo misto, è possibile serializzare il tasto cancelletto:

Array.prototype.getUnique = function() {
    var hash = {}, result = [], key; 
    for ( var i = 0, l = this.length; i < l; ++i ) {
        key = JSON.stringify(this[i]);
        if ( !hash.hasOwnProperty(key) ) {
            hash[key] = true;
            result.push(this[i]);
        }
    }
    return result;
}

5

Per risolvere il problema viceversa, può essere utile non avere duplicati durante il caricamento dell'array, come farebbe Set object ma non è ancora disponibile in tutti i browser. Risparmia memoria ed è più efficiente se devi guardarne il contenuto più volte.

Array.prototype.add = function (elem) {
   if (this.indexOf(elem) == -1) {
      this.push(elem);
   }
}

Campione:

set = [];
[1,3,4,1,2,1,3,3,4,1].forEach(function(x) { set.add(x); });

Ti dà set = [1,3,4,2]

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.