Holen Sie sich alle eindeutigen Werte in einem JavaScript-Array (entfernen Sie Duplikate)


1485

Ich habe eine Reihe von Zahlen, die ich sicherstellen muss, dass sie eindeutig sind. Ich habe das Code-Snippet unten im Internet gefunden und es funktioniert großartig, bis das Array eine Null enthält. Ich habe dieses andere Skript gefunden hier auf Stack Overflow gefunden, das fast genau so aussieht, aber nicht fehlschlägt.

Kann mir jemand helfen, festzustellen, wo das Prototyp-Skript falsch läuft, um mir beim Lernen zu helfen?

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

Weitere Antworten von doppelten Fragen:

Ähnliche Frage:


3
@hippietrail Bei dieser älteren Frage geht es darum, nur die Duplikate zu finden und zurückzugeben (ich war auch verwirrt!). Meine Frage ist eher, warum diese Funktion fehlschlägt, wenn ein Array eine Null enthält.
Mottie

Sie möchten wahrscheinlich auch Ihren Fragentitel weniger vage machen.
Hippietrail

Wenn zukünftige Leser feststellen, dass Sie den Inhalt Ihrer Datenstruktur ständig algorithmisch ändern müssen (ordnen, wiederholende Elemente entfernen usw.) oder bei jeder Iteration nach darin enthaltenen Elementen suchen müssen, können Sie davon ausgehen, dass Sie dies tun Verwenden Sie zunächst die falsche Datenstruktur und verwenden Sie eine, die für die jeweilige Aufgabe besser geeignet ist (in diesem Fall ein Hash-Set anstelle eines Arrays).
Nurettin

Ich habe den Code vor ein paar Jahren von einem anderen Ort kopiert ... aber es scheint ziemlich einfach zu sein: o= object, a= array, i= indexund e= umm, etwas: P
Mottie

Antworten:


2660

Mit JavaScript 1.6 / ECMAScript 5 können Sie die native filterMethode eines Arrays folgendermaßen verwenden, um ein Array mit eindeutigen Werten abzurufen:

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

Die native Methode filterdurchläuft das Array und hinterlässt nur die Einträge, die die angegebene Rückruffunktion bestehen onlyUnique.

onlyUniqueprüft, ob der angegebene Wert der erste ist, der auftritt. Wenn nicht, muss es ein Duplikat sein und wird nicht kopiert.

Diese Lösung funktioniert ohne zusätzliche Bibliothek wie jQuery oder prototype.js.

Es funktioniert auch für Arrays mit gemischten Werttypen.

Für alten Browser (<ie9), das nicht unterstützt die nativen Methoden filterund indexOfSie können Workarounds in der MDN - Dokumentation finden Filter und indexOf .

Wenn Sie das letzte Vorkommen eines Werts beibehalten möchten, ersetzen Sie ihn einfach indexOfdurchlastIndexOf .

Mit ES6 könnte dies verkürzt werden:

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

Vielen Dank an Camilo Martin für den Hinweis im Kommentar.

ES6 verfügt über ein natives Objekt Setzum Speichern eindeutiger Werte. Um ein Array mit eindeutigen Werten zu erhalten, können Sie jetzt Folgendes tun:

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

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

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

Der Konstruktor von Setnimmt ein iterierbares Objekt wie Array, und der Spread-Operator ...wandelt die Menge wieder in ein Array um. Vielen Dank an Lukas Liese für den Hinweis im Kommentar.


53
Diese Lösung läuft leider viel langsamer. Sie schleifen zweimal, einmal mit Filter und einmal mit Index von
Jack Franzen

18
@ JackFranzen Langsamer als was? Die Lösung von Rafael ? Die Rafaels- Lösung funktioniert nicht für Arrays vom gemischten Typ. Für mein Beispiel ['a', 1, 'a', 2, '1']würden Sie bekommen ['a', 1, 2]. Aber das habe ich nicht erwartet. Übrigens ist viel langsamer sehr relativ.
TLindig

25
In der modernen JS: .filter((v,i,a)=>a.indexOf(v)==i)(fette Pfeilnotation).
Camilo Martin

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

5
Eine viel detailliertere Antwort, einschließlich vieler Möglichkeiten - wie zuerst sortieren und mit unterschiedlichen Datentypen umgehen
Dan Nissenbaum

874

Aktualisierte Antwort für ES6 / ES2015 : Mit dem Set lautet die einzeilige Lösung:

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

Welches kehrt zurück

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

Wie von le_m vorgeschlagen, kann dies auch mit dem Spread-Operator wie z

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

10
Beachten Sie, dass das innere Array nicht funktionieren würdeArray.from(new Set([[1,2],[1,2],[1,2,3]]))
Alexander Goncharov

2
Leistung dieser Lösung im Vergleich zu myArray.filter((v, i, a) => a.indexOf(v) === i);?
Simoyw

42
Beachten Sie, dass wenn Sie die SetObjekte und anstelle von primitiven Werten hinzufügen, eindeutige Verweise auf die Objekte enthalten. Das Set- sIn gibt also let s = new Set([{Foo:"Bar"}, {Foo:"Bar"}]);Folgendes zurück: Dies Set { { Foo: 'Bar' }, { Foo: 'Bar' } }ist ein Setmit eindeutigen Objektverweisen auf Objekte, die dieselben Werte enthalten. Wenn Sie schreiben let o = {Foo:"Bar"};und dann ein Set mit zwei Referenzen wie let s2 = new Set([o,o]);folgt erstellen : dann wird s2Set { { Foo: 'Bar' } }
mortb

2
Beide Optionen haben Probleme mit IE10, selbst wenn eine polyfill.io-Ebene vorhanden ist
Thymine,

2
neuer Testfall jsperf.com/array-filter-unique-vs-new-set/1 scheint wie new Set's Trophäe
Shuk

167

Mir ist klar, dass diese Frage bereits mehr als 30 Antworten hat. Aber ich habe zuerst alle vorhandenen Antworten durchgelesen und meine eigenen Nachforschungen angestellt.

Ich habe alle Antworten auf 4 mögliche Lösungen aufgeteilt:

  1. Verwenden Sie die neue ES6-Funktion: [...new Set( [1, 1, 2] )];
  2. Verwenden Sie Objekt { }, um Duplikate zu vermeiden
  3. Verwenden Sie das Helper-Array [ ]
  4. Verwenden filter + indexOf

Hier sind Beispielcodes in den Antworten:

Verwenden Sie die neue ES6-Funktion: [...new Set( [1, 1, 2] )];

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

Verwenden Sie Objekt { }, um Duplikate zu vermeiden

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

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

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

Verwenden Sie das Helper-Array [ ]

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

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

Und ich habe mich gefragt, welches schneller ist. Ich habe ein Beispiel für Google Sheet erstellt , um Funktionen zu testen. Hinweis: ECMA 6 ist in Google Sheets nicht verfügbar, daher kann ich es nicht testen.

Hier ist das Ergebnis von Tests: Geben Sie hier die Bildbeschreibung ein

Ich habe erwartet, dass Code mit Objekt { }gewinnt, weil er Hash verwendet. Daher bin ich froh, dass Tests die besten Ergebnisse für diesen Algorithmus in Chrome und IE gezeigt haben. Danke an @rab für den Code .


Die Option "filter + indexOf" ist bei Arrays über 100.000 Elementen extrem langsam. Ich musste den "Objektkarten" -Ansatz verwenden, der jedoch die ursprüngliche Sortierung unterbricht.
Liberborn

Ist eine höhere Zahl langsamer, nicht schneller?
Fletchsod

3
@ fletchsod, Zahlen sind die Zeit in ms, um den Code auszuführen.
Max Makhrov

1
Die Zahlen sind falsch! Das Skript für Google Apps wird auf dem Server und nicht im Browser des Clients ausgeführt. Daher ist die Leistung in Chrome / IE (oder praktisch jedem Browser) gleich!
Jay Dadhania

1
Während es möglich ist , die clientseitige JS von Google Script (entweder über laufen google.script.host oder google.script.run - nicht sicher , welche) heißt die Antwort eindeutig , dass „ECMA 6 nicht lieferbar in Google Tabellen ist“ - also Wir können davon ausgehen, dass das Poster dafür serverseitiges Google Script verwendet hat - und daher zeigt die Antwort die Leistung des Google Servers, nicht des Browsers !!
Jay Dadhania

134

Sie können auch underscore.js verwenden .

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

welches zurückkehren wird:

[1, 2, 3, 4]

22
Bitte machen Sie diese Leute. Stecken Sie nichts auf den Array-Prototyp. Bitte.
Jacob Dalton

5
@JacobDalton - Dies erweitert den Array-Prototyp nicht. Der Namespace im _ -Objekt.
Superluminary

24
@superluminary Ich weiß, deshalb habe ich gesagt, bitte mach das. Die akzeptierte Lösung schlägt vor, den Array-Prototyp zu ändern. Tu das nicht.
Jacob Dalton

20
@JacobDalton Bitte tu das nicht. Es ist nicht erforderlich, eine zusätzliche Bibliothek hinzuzufügen, nur für einen kleinen Job, der mitarray = [...new Set(array)]

3
Dies hat den Zweck der Verwendung von ES-Funktionen zunichte gemacht und das Hinzufügen weiterer Bibliotheken hat die Website nur noch mehr aufgebläht.
Fletchsod

68

Ein Liner, reines JavaScript

Mit ES6-Syntax

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")

Geben Sie hier die Bildbeschreibung ein

Mit ES5-Syntax

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

Browserkompatibilität : IE9 +


13
@ Spets Es hat quadratische Kosten, also ist es nicht die beste Antwort
Oriol

@Spets wie alle Unikate / Unterscheidungsmerkmale ... ja, seien Sie schlau, verwenden Sie Radix, um zuerst zu sortieren, und sind Sie in den meisten Fällen langsamer als 0 (n2).
Doker

danke Leute! Ich habe es nicht bemerkt, als ich mir den Code zum ersten Mal angesehen habe, aber jetzt ist es etwas offensichtlicher.
Spets

51

Ich habe seitdem eine nette Methode gefunden, die jQuery verwendet

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

Hinweis: Dieser Code wurde aus Paul Irishs Entenstanzpfosten gezogen - ich habe vergessen, eine Gutschrift zu geben: P.


8
Eine prägnante Lösung, aber das Aufrufen von inArray ist weitaus weniger effizient als das Aufrufen von hasOwnProperty.
Mister Smith

Das ist auch O (N ^ 2), oder? Während der Dictionary- oder hasOwnProperty-Ansatz wahrscheinlich O (N * logN) wäre.
Speedplane

Dies funktionierte für mich, alle anderen Lösungen wurden von Internet Explorer nicht unterstützt.
Kerl

51

Kürzeste Lösung mit ES6: [...new Set( [1, 1, 2] )];

Oder wenn Sie den Array-Prototyp ändern möchten (wie in der ursprünglichen Frage):

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

EcmaScript 6 ist derzeit (August 2015) nur teilweise in modernen Browsern implementiert , Babel jedoch ist sehr beliebt geworden, um ES6 (und sogar ES7) zurück auf ES5 zu übertragen. Auf diese Weise können Sie heute ES6-Code schreiben!

Wenn Sie sich fragen, was das ...bedeutet, wird es als Spread-Operator bezeichnet . Von MDN : «Mit dem Spread-Operator kann ein Ausdruck an Stellen erweitert werden, an denen mehrere Argumente (für Funktionsaufrufe) oder mehrere Elemente (für Array-Literale) erwartet werden». Da ein Set iterierbar ist (und nur eindeutige Werte haben kann), erweitert der Spread-Operator das Set, um das Array zu füllen.

Ressourcen zum Lernen von ES6:


3
Sie können es sogar noch kürzer machen, a = [...Set(a)]aber dies ist vorerst nur Firefox.
c69

@ c69, richtig, wird nicht kürzer. SpiderMonkey-Benutzer werden es ebenfalls zu schätzen wissen.
Torsten Becker

Arbeitet mit Babel. Sie müssen nur Array.from Shim auch einschließen:require ( "core-js/fn/array/from" );
Vladislav Rastrusny

sollte es Array.prototype.getUnique = function () {return [... new Set ([this])] sein; }; oder Array.prototype.getUnique = function () {return [... new Set (this)]; }; Ich habe es auf das folgende $ ('body'). Html (). ToLowerCase (). Match (/ ([a-zA-Z0-9 ._ + -] + @ [a-zA-Z0-9) angewendet. _-] + \. [a-zA-Z0-9 ._-] +) / gi) .getUnique (); Der erste gab mir nur einen eindeutigen Wert zurück, wobei der zweite alle entfernten Duplikate neu abstimmte.
Ashique

[...Set(['a', 1, 'a', 2, '1'])]wird einen TypeError werfen, daher ist es immer noch ratsam, Folgendes beizubehalten new:[...new Set(['a', 1, 'a', 2, '1'])]
Adam Katz

36

Einfachste Lösung:

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

Oder:

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


4
Beachten Sie, dass dies unter IE10 und darunter nicht unterstützt wird. Beachten Sie, dass IE11 + und Safari nur eingeschränkten Support bieten (nicht einmal sicher, ob das obige Beispiel funktioniert). Developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
oriadam

32

Der einfachste und schnellste (in Chrome) Weg, dies zu tun:

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

Durchläuft einfach jedes Element im Array, testet, ob dieses Element bereits in der Liste enthalten ist, und drückt es auf das Array, das zurückgegeben wird, wenn dies nicht der Fall ist.

Laut jsPerf ist diese Funktion die schnellste, die ich jemals finden konnte - Sie können jedoch auch Ihre eigene hinzufügen.

Die Nicht-Prototyp-Version:

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

Sortierung

Wenn Sie das Array auch sortieren müssen, ist Folgendes am schnellsten:

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

oder Nicht-Prototyp:

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

Dies ist in den meisten Nicht-Chrome-Browsern auch schneller als die oben beschriebene Methode .


Unter Linux bevorzugt Chrome 55.0.2883 Ihre arr.unique () und swilliams 'arrclone2.sortFilter () ist am langsamsten (78% langsamer). In Firefox 51.0.0 (mit vielen Addons) sind Swilliams jedoch am schnellsten (und dennoch um Ops / Sek. Langsamer als jedes andere Chrome-Ergebnis), wobei Motties jQuery $ .grep (arr, jqFilter) am langsamsten ist (46% langsamer). Ihr arr.uniq () war 30% langsamer. Ich habe jeden Test zweimal durchgeführt und konsistente Ergebnisse erzielt. Rafaels arr.getUnique () belegte in beiden Browsern den zweiten Platz.
Adam Katz

jsPerf ist Buggy im Moment, so meine Bearbeitung auf diesen Test nicht alles begangen hat, aber es tat Ergebnis in dem Hinzufügen von zwei Tests: Cocco der toUnique () schlägt Vamsi der ES6 list.filter () auf beiden Browsern, swilliams' sortFilter schlagen () für # 1 auf FF (sortFilter war 16% langsamer) und schlagen Sie Ihre sortierten Tests (die um 2% langsamer waren) für # 3 auf Chrome.
Adam Katz

Ah, ich hatte nicht bemerkt, dass diese Tests trivial klein waren und keine Rolle spielten. Ein Kommentar zur akzeptierten Antwort beschreibt dieses Problem und bietet eine Korrektur in einer Überarbeitung des Tests, bei der Rafaels Code leicht der schnellste und Joetje50s arr.unique-Code 98% langsamer ist. Ich habe auch eine weitere Überarbeitung vorgenommen, wie in diesem Kommentar angegeben .
Adam Katz

1
Nun, tatsächlich hat der Algorithmus, den Sie in der uniqueFunktion implementiert haben, eine Komplexität von O (n ^ 2), während der in getUniqueO (n) ist. Der erste mag bei kleinen Datenmengen schneller sein, aber wie können Sie mit der Mathematik argumentieren :) Sie können sicherstellen, dass der letztere schneller ist, wenn Sie ihn auf einer Reihe von beispielsweise 1e5 einzigartigen Elementen ausführen
Mikhail Dudin

31

NUR LEISTUNG! Dieser Code ist wahrscheinlich 10x schneller als alle Codes hier * funktioniert in allen Browsern und hat auch die geringste Auswirkung auf den Speicher .... und mehr

Wenn Sie das alte Array nicht wiederverwenden müssen, müssen Sie übrigens die erforderlichen anderen Vorgänge ausführen, bevor Sie es in ein einzigartiges Array konvertieren. Dies ist wahrscheinlich der schnellste Weg, um dies zu tun, auch sehr kurz.

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

dann können Sie dies versuchen

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]

Ich habe mir diese Funktion ausgedacht, als ich diesen Artikel gelesen habe ...

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

Ich mag die for-Schleife nicht. Es hat zu viele Parameter. Ich mag die while-- Schleife. while ist die schnellste Schleife in allen Browsern außer der, die wir alle so sehr mögen ... Chrome.

Trotzdem habe ich die erste Funktion geschrieben, die while verwendet. Und ja, sie ist etwas schneller als die im Artikel gefundene Funktion. Aber nicht genug.unique2()

nächster schritt moderne js verwenden. Object.keys Ich habe die andere for-Schleife durch die Object.keys von js1.7 ersetzt ... etwas schneller und kürzer (in Chrome 2x schneller);). Nicht genug!. unique3().

Zu diesem Zeitpunkt dachte ich darüber nach, was ich wirklich in MEINER einzigartigen Funktion brauche. Ich brauche das alte Array nicht, ich möchte eine schnelle Funktion. Also habe ich 2 while-Schleifen + Spleißen verwendet.unique4()

Es ist nutzlos zu sagen, dass ich beeindruckt war.

Chrom: Die üblichen 150.000 Operationen pro Sekunde stiegen auf 1.800.000 Operationen pro Sekunde.

dh: 80.000 op / s gegenüber 3.500.000 op / s

ios: 18.000 op / s vs 170.000 op / s

Safari: 80.000 op / s vs 6.000.000 op / s

Beweis http://jsperf.com/wgu oder besser console.time ... microtime ... was auch immer verwenden

unique5() soll Ihnen nur zeigen, was passiert, wenn Sie das alte Array behalten möchten.

Verwenden Sie nicht, Array.prototypewenn Sie nicht wissen, was Sie tun. Ich habe gerade viel kopiert und vergangen. Verwenden Object.defineProperty(Array.prototype,...,writable:false,enumerable:false})Sie diese Option, wenn Sie einen nativen Prototyp erstellen möchten. Beispiel: https://stackoverflow.com/a/20463021/2450730

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

HINWEIS: Ihr altes Array wird nach diesem Vorgang zerstört / wird einzigartig.

Wenn Sie den obigen Code nicht lesen können, lesen Sie ein Javascript-Buch oder hier finden Sie einige Erklärungen zu kürzerem Code. https://stackoverflow.com/a/21353032/2450730

Einige verwenden indexOf ... nicht ... http://jsperf.com/dgfgghfghfghghgfhgfhfghfhgfh

für leere Arrays

!array.length||toUnique(array); 

4
getestet auf node.js mit einem 100k-Array von Urls (Strings). Das Ergebnis war 2x langsamer als underscore.js _.uniq ... obwohl ein separater jsperf mit Ihnen übereinstimmt ( jsperf.com/uniq-performance/5 ), bin ich enttäuscht :(
xShirase

3
Sie testen in jsperf nicht richtig ... in Ihrem Beispiel definieren Sie die Funktion jedes Mal ... aber die Funktionen von underscore.js sind bereits definiert. Dies bestraft meine Funktion. Testen Sie auch 3 und 4. Eine andere Sache, die ich sagen sollte, ist, dass Sie, wenn Sie gemischte Variablen (Zeichenfolgen und Zahlen) verwenden, a [b]! == a [c] durch a [b]! = a [c]
cocco

2
Ich bin mir nicht sicher, ob der jsPerf korrigiert wurde, aber es scheint, dass die Reduce-Alternative (die für mich leichter zu verstehen ist) etwas 94% schneller ist als Ihre Lösung. jsperf.com/reduce-for-distinct Bearbeiten: Ihre ist bei Chrome langsamer, bei Edge gleich und bei Firefox schneller.
Victor Ivens

3
Nur mit kleinen Arrays / geringem Prozentsatz an Duplikaten verwendbar . Jedes Mal, wenn ein nicht eindeutiges Objekt gefunden wurde, wird die Engine gezwungen, alle restlichen Elemente nach links zu verschieben, indem: 1) über sie iteriert wird 2) sie gelesen werden 3) sie an eine neue Position geschrieben werden. Und dann erstellt es auch ein neues Array mit diesem einen gespleißten Element und gibt es als Ergebnis zurück ... Der Algorithmus verschlechtert sich schnell mit größeren Arrays / häufigeren Duplikaten. Für Arrays mit einer Größe von 1000-> 10000-> 50000 und ~ 40% Duplikaten beträgt die durchschnittlich benötigte Zeit 2-> 775-> 19113 ms. (Größe ändert sich als 1-> x10-> x5, Zeit als 1-> x10-> x25) in Chrome.
Ankhzet

1
Vielen Dank. Netter Ansatz. Aber was ist mit der richtigen Artikelbestellung?
Liberborn

28

Viele der Antworten hier sind für Anfänger möglicherweise nicht hilfreich. Wenn es schwierig ist, ein Array zu de-dupen, wissen sie dann wirklich etwas über die Prototypenkette oder sogar über jQuery?

In modernen Browsern besteht eine saubere und einfache Lösung darin, Daten in einem Set zu speichern , das als Liste eindeutiger Werte konzipiert ist.

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

Dies Array.fromist nützlich, um das Set wieder in ein Array zu konvertieren, damit Sie einfachen Zugriff auf alle fantastischen Methoden (Funktionen) von Arrays haben. Es gibt auch andere Möglichkeiten , dasselbe zu tun. Möglicherweise benötigen Sie dies jedoch überhaupt nicht Array.from, da Sets zahlreiche nützliche Funktionen wie forEach enthalten .

Wenn Sie den alten Internet Explorer unterstützen müssen und Set daher nicht verwenden können, besteht eine einfache Methode darin, Elemente in ein neues Array zu kopieren, während Sie zuvor prüfen, ob sie sich bereits im neuen Array befinden.

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

Um dies sofort wiederverwendbar zu machen, fügen wir es in eine Funktion ein.

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

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

        return result;
    }
}

Um die Duplikate loszuwerden, würden wir dies jetzt tun.

var uniqueCars = deduplicate(cars);

Der deduplicate(cars)Teil wird zu dem, was wir als Ergebnis bezeichnet haben das wenn die Funktion abgeschlossen ist.

Übergeben Sie einfach den Namen eines beliebigen Arrays.


Übrigens habe ich ein Array voller Strings verwendet, um zu zeigen, dass meine Technik flexibel ist. Es wird richtig für Zahlen funktionieren.
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;
 }, []);

Können Sie erklären, warum Sie pushdas Element nicht einfach auf das Array gesetzt haben, anstatt es zu verwenden concat? Ich habe versucht, Push zu verwenden, und es ist fehlgeschlagen. Ich suche eine Erklärung.
Makenova

1
Der Grund liegt im Rückgabewert. concat gibt das geänderte Array zurück (genau das, was innerhalb der Reduktionsfunktion zurückgegeben werden muss), während push einen Index zurückgibt, an dem Sie auf den Push-Wert zugreifen können. Beantwortet das deine Frage?
Sergeyz

Anscheinend ist diese Lösung in Chrom (und Knoten) ziemlich schnell. jsperf.com/reduce-for-distinct Das ist die ES6-Version: [0,1,2,0,3,2,1,5].reduce((prev, cur) => ~prev.indexOf(cur) ? prev : prev.concat([cur]), []);
Victor Ivens

Randnotiz: Diese Implementierung ist nicht NaNfreundlich
Alireza

19

Wir können dies mit ES6-Sets tun:

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

console.log(uniqueArray);

// Die Ausgabe wird sein

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

17

Dieser Prototyp getUniqueist nicht ganz korrekt, denn wenn ich ein Array wie das folgende habe: ["1",1,2,3,4,1,"foo"]Es wird zurückgegeben ["1","2","3","4"]und "1"ist ein String und 1eine Ganzzahl. Sie sind anders.

Hier ist eine richtige Lösung:

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

mit:

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

Das obige wird produzieren ["1",2,3,4,1,"foo"].


1
Beachten Sie, dass dies $foo = 'bar'die PHP-Methode zum Deklarieren von Variablen ist. Es funktioniert in Javascript, erstellt jedoch ein implizites globales Element und sollte im Allgemeinen nicht durchgeführt werden.
Camilo Martin

@CamiloMartin Entschuldigung, aber Sie liegen falsch. $ Foo ist global, da das Beispiel nicht geschlossen ist und ihm das Schlüsselwort var fehlt. Nichts mit dem Dollar zu tun jsfiddle.net/robaldred/L2MRb
Rob

8
@Rob das ist genau das, was ich sage, PHP-Leute werden denken, $fooist die Art und Weise, Variablen in Javascript zu deklarieren, während dies tatsächlich der Fall var fooist.
Camilo Martin

13

Ohne Array.prototype zu erweitern (es wird als schlechte Praxis bezeichnet) oder jquery / underscore zu verwenden, können Sie filterdas Array einfach erweitern .

Durch Beibehalten des letzten Auftretens:

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

oder erstes Auftreten:

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

Nun, es ist nur Javascript ECMAScript 5+, was nur IE9 + bedeutet, aber es ist gut für eine Entwicklung in nativem HTML / JS (Windows Store App, Firefox OS, Sencha, Phonegap, Titan, ...).


2
Die Tatsache, dass es sich um js 1.6 handelt, bedeutet nicht, dass Sie es nicht verwenden können filter. Auf der MDN-Seite haben sie eine Implementierung für Internet Explorer, ich meine ältere Browser. Außerdem: JS 1.6 bezieht sich nur auf die js-Engine von Firefox, aber das Richtige ist, dass es sich um ECMAScript 5 handelt.
Camilo Martin

@CamiloMartin Ich habe 1.6 in ECMAScript5 geändert. Vielen Dank.
Cœur

13

Magie

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

O (n) Leistung ; Wir gehen davon aus, dass sich Ihr Array in aund befindet t={}. Erklärung hier (+ Jeppe Impr.)


14
Das sieht so super cool aus, dass du ohne eine solide Erklärung gefallen bist. Du wirst meine Bitcoins
abbauen,

3
Was ich damit gemeint habe ist, dass Sie Ihre Antwort mit einer Erklärung und einer kommentierten Dekonstruktion erweitern sollten. Erwarten Sie nicht, dass die Leute solche nützlichen Antworten finden. (obwohl es wirklich cool aussieht und wahrscheinlich funktioniert)
Ondřej Želazko

1
@Jeppe Wenn ich Ihre Verbesserung durchführe, tritt ein Aha-Effekt auf (bevor ich nicht weiß, dass ich einen inOperator außerhalb der anderen Konstruktion als forSchleife verwenden kann: P) - Danke - ich weiß das zu schätzen und werde Ihren anderen guten Antworten +2 geben .
Kamil Kiełczewski

2
Erstellt das nicht eine globale Variable t, die nach dem Filtern am Leben bleibt?
Philipp

1
Das ist wirklich eine knifflige Lösung. Leider müssen Sie t außerhalb des Ausdrucks deklarieren, es sei denn, Sie möchten eine globale Variable definieren und / oder sich darauf verlassen, dass Ihr Modulbündler die globale Variable ausblendet. Es sieht schick aus, aber tun Sie sich selbst (und Ihrem Entwicklerteam) einen Gefallen und schreiben Sie dann einfach einen Zwei-Liner: let t = {}; a.filter (e =>! (t [e] = e in t))
ford04

13
[...new Set(duplicates)]

Dies ist die einfachste und wird in MDN-Webdokumenten referenziert .

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]

Während dieser Code die Frage lösen kann, einschließlich einer Erklärung, wie und warum dies das Problem löst, würde dies wirklich dazu beitragen, die Qualität Ihres Beitrags zu verbessern, und wahrscheinlich zu mehr Up-Votes führen. Denken Sie daran, dass Sie in Zukunft die Frage für die Leser beantworten, nicht nur für die Person, die jetzt fragt. Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung hinzuzufügen, und geben Sie an, welche Einschränkungen und Annahmen gelten.
id.ot

11

Wenn Sie das Prototype-Framework verwenden, müssen keine 'for'-Schleifen ausgeführt werden. Sie können http://www.prototypejs.org/api/array/uniq folgendermaßen verwenden :

var a = Array.uniq();  

Dadurch wird ein doppeltes Array ohne Duplikate erstellt. Ich bin auf Ihre Frage gestoßen, als ich nach einer Methode gesucht habe, um verschiedene Array-Datensätze zu zählen

uniq ()

ich benutzte

Größe()

und da war mein einfaches Ergebnis. ps Entschuldigung, wenn ich etwas falsch geschrieben habe

Bearbeiten: Wenn Sie undefinierte Datensätze maskieren möchten, möchten Sie diese möglicherweise hinzufügen

kompakt()

vorher so:

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

14
weil ich eine bessere Antwort gefunden habe, denke ich, dass Themen für alle Menschen sind, nicht nur für diejenigen, die gefragt haben
Decebal

11

Mit Sets können Sie jetzt Duplikate entfernen und wieder in das Array konvertieren.

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

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

Eine andere Lösung ist die Verwendung von Sortieren und Filtern

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

Das 0liegt daran, dass es sich bei JavaScript um einen falschen Wert handelt.

this[i] wird falsch sein, wenn der Wert des Arrays 0 oder ein anderer falscher Wert ist.


Ahhhh, ok, ich verstehe jetzt ... aber würde es eine einfache Lösung geben, damit es funktioniert?
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
}

Ich denke, dass dies nicht funktioniert, wenn das Array Objekte / Arrays enthält, und ich bin nicht sicher, ob es den Skalartyp beibehält.
Camilo Martin

Ja, alles wird streng. Dies könnte behoben werden, indem der ursprüngliche Wert in ostatt nur in a gespeichert wird 1, obwohl der Gleichheitsvergleich immer noch stringweise wäre (obwohl er von allen möglichen Javascript-Gleichheiten nicht allzu unvernünftig erscheint).
Ephemient

Der Array.prototype konnte nur mit nicht aufzählbaren Methoden erweitert werden .... Object.defineProperty (Array.prototype, "getUnique", {}) ... aber die Idee, ein
Hilfsobjekt zu verwenden,

10

Ich hatte ein etwas anderes Problem, bei dem ich Objekte mit doppelten ID-Eigenschaften aus einem Array entfernen musste. das hat funktioniert.

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

Die einfachste Antwort lautet:

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

Einfach, aber es funktioniert nicht mit einer Reihe von Objekten.
Thanwa Ch.

7

Ich bin mir nicht sicher, warum Gabriel Silveira die Funktion so geschrieben hat, aber eine einfachere Form, die für mich genauso gut und ohne Minimierung funktioniert, ist:

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

oder in CoffeeScript:

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

7

Wenn Sie mit zusätzlichen Abhängigkeiten einverstanden sind oder bereits eine der Bibliotheken in Ihrer Codebasis haben, können Sie mit LoDash (oder Underscore) Duplikate aus einem vorhandenen Array entfernen.

Verwendungszweck

Wenn Sie es noch nicht in Ihrer Codebasis haben, installieren Sie es mit npm:

npm install lodash

Verwenden Sie es dann wie folgt:

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

Aus:

[ 1, 2, 3 ]

7

Dies wurde viel beantwortet, aber es wurde nicht auf meine besonderen Bedürfnisse eingegangen.

Viele Antworten lauten wie folgt:

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

Dies funktioniert jedoch nicht für Arrays komplexer Objekte.

Angenommen, wir haben ein Array wie dieses:

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

Wenn wir die Objekte mit eindeutigen Namen haben möchten, sollten wir array.prototype.findIndexanstelle von array.prototype.indexOf:

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

Tolle Lösung, Vorsicht, dass ein neues Array von einer Funktion zurückkehrt. (es ändert sich nicht selbst)
Thanwa Ch.

6

Aus Shamasis Bhattacharyas Blog (O (2n) Zeitkomplexität):

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

Aus Paul Irishs Blog : Verbesserung von 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

Finden eindeutiger Array-Werte in einer einfachen Methode

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

Es scheint, dass wir Rafaels Antwort verloren haben , die einige Jahre lang als akzeptierte Antwort galt. Dies war (zumindest 2017) die leistungsstärkste Lösung, wenn Sie kein Array vom gemischten Typ haben :

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

Wenn Sie tun , ein Array Mischtyp haben, können Sie die Raute - Taste serialisiert:

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

Um das Problem umgekehrt zu lösen, kann es nützlich sein, beim Laden Ihres Arrays kein Duplikat zu haben, so wie es das Set- Objekt tun würde, aber es ist noch nicht in allen Browsern verfügbar. Es spart Speicher und ist effizienter, wenn Sie den Inhalt mehrmals betrachten müssen.

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

Stichprobe:

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

Gibt Ihnen set = [1,3,4,2]

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.