Frage Entfernen Sie doppelte Werte aus dem JS-Array [duplizieren]


Diese Frage hat hier bereits eine Antwort:

Ich habe ein sehr einfaches JavaScript-Array, das Duplikate enthalten kann oder auch nicht.

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

Ich muss die Duplikate entfernen und die eindeutigen Werte in ein neues Array einfügen.

Ich könnte auf alle Codes zeigen, die ich ausprobiert habe, aber ich denke, es ist nutzlos, weil sie nicht funktionieren. Ich akzeptiere auch jQuery-Lösungen.

Ähnliche Frage:


861
2018-02-10 14:53


Ursprung


Antworten:


Schnell und schmutzig mit jQuery:

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
var uniqueNames = [];
$.each(names, function(i, el){
    if($.inArray(el, uniqueNames) === -1) uniqueNames.push(el);
});

362
2018-02-10 15:13



"Smart" aber naiv

uniqueArray = a.filter(function(item, pos) {
    return a.indexOf(item) == pos;
})

Im Grunde iterieren wir über das Array und prüfen für jedes Element, ob die erste Position dieses Elements im Array der aktuellen Position entspricht. Offensichtlich sind diese beiden Positionen für doppelte Elemente unterschiedlich.

Mit dem dritten ("this array") Parameter des Filter-Callbacks können wir eine Schließung der Array-Variablen vermeiden:

uniqueArray = a.filter(function(item, pos, self) {
    return self.indexOf(item) == pos;
})

Obwohl kurz gefasst, ist dieser Algorithmus nicht besonders effizient für große Arrays (quadratische Zeit).

Hashtables zur Rettung

function uniq(a) {
    var seen = {};
    return a.filter(function(item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}

So wird es normalerweise gemacht. Die Idee besteht darin, jedes Element in eine Hashtabelle einzufügen und dann sofort auf seine Anwesenheit zu prüfen. Dies gibt uns lineare Zeit, hat aber mindestens zwei Nachteile:

  • Da Hash-Schlüssel nur Zeichenfolgen in Javascript sein können, unterscheidet dieser Code nicht zwischen Zahlen und "numerischen Zeichenfolgen". Das ist, uniq([1,"1"]) werde nur zurückkommen [1]
  • Aus dem gleichen Grund werden alle Objekte als gleich angesehen: uniq([{foo:1},{foo:2}]) werde nur zurückkommen [{foo:1}].

Wenn Ihre Arrays jedoch nur Primitive enthalten und Sie sich nicht um Typen kümmern (z. B. sind es immer Zahlen), ist diese Lösung optimal.

Das Beste aus zwei Welten

Eine universelle Lösung kombiniert beide Ansätze: Sie verwendet Hash-Lookups für Primitive und lineare Suche nach Objekten.

function uniq(a) {
    var prims = {"boolean":{}, "number":{}, "string":{}}, objs = [];

    return a.filter(function(item) {
        var type = typeof item;
        if(type in prims)
            return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
        else
            return objs.indexOf(item) >= 0 ? false : objs.push(item);
    });
}

sortieren | uniq

Eine andere Möglichkeit besteht darin, zuerst das Array zu sortieren und dann jedes Element gleich dem vorherigen zu entfernen:

function uniq(a) {
    return a.sort().filter(function(item, pos, ary) {
        return !pos || item != ary[pos - 1];
    })
}

Auch dies funktioniert nicht mit Objekten (weil alle Objekte gleich sind für sort). Darüber hinaus ändern wir still das ursprüngliche Array als Nebeneffekt - nicht gut! Wenn Ihre Eingabe jedoch bereits sortiert ist, ist dies der richtige Weg (entfernen Sie einfach sort von Oben).

Einzigartig von ...

Manchmal ist es erwünscht, eine Liste auf der Basis anderer Kriterien als nur der Gleichheit zu vereinheitlichen, beispielsweise um Objekte auszusortieren, die unterschiedlich sind, aber eine Eigenschaft teilen. Dies kann elegant durch Übergabe eines Callbacks erfolgen. Dieser "Schlüssel" -Rückruf wird auf jedes Element angewendet, und Elemente mit gleichen "Schlüsseln" werden entfernt. Schon seit key Es wird erwartet, dass eine primitive Hash-Tabelle zurückgegeben wird.

function uniqBy(a, key) {
    var seen = {};
    return a.filter(function(item) {
        var k = key(item);
        return seen.hasOwnProperty(k) ? false : (seen[k] = true);
    })
}

Ein besonders nützliches key() ist JSON.stringify wodurch Objekte entfernt werden, die sich physisch unterscheiden, aber "gleich aussehen":

a = [[1,2,3], [4,5,6], [1,2,3]]
b = uniqBy(a, JSON.stringify)
console.log(b) // [[1,2,3], [4,5,6]]

Wenn die key ist nicht primitiv, man muss auf die lineare Suche zurückgreifen:

function uniqBy(a, key) {
    var index = [];
    return a.filter(function (item) {
        var k = key(item);
        return index.indexOf(k) >= 0 ? false : index.push(k);
    });
}

oder benutze die Set Objekt in ES6:

function uniqBy(a, key) {
    var seen = new Set();
    return a.filter(item => {
        var k = key(item);
        return seen.has(k) ? false : seen.add(k);
    });
}

(Manche Leute bevorzugen !seen.has(k) && seen.add(k) Anstatt von seen.has(k) ? false : seen.add(k)).

Bibliotheken

Beide unterstreichen und Lo-Strich zu Verfügung stellen uniq Methoden. Ihre Algorithmen sind im Grunde genommen ähnlich wie die oben genannten ersten Abschnitte und gehen auf Folgendes ein:

var result = [];
a.forEach(function(item) {
     if(result.indexOf(item) < 0) {
         result.push(item);
     }
});

Dies ist quadratisch, aber es gibt nette zusätzliche Goodies, wie das Wrapping nativ indexOfFähigkeit, sich mit einem Schlüssel zu vereinheitlichen (iteratee in ihrem Sprachgebrauch) und Optimierungen für bereits sortierte Arrays.

Wenn Sie jQuery verwenden und nichts ohne einen Dollar davor ertragen können, geht es so:

  $.uniqArray = function(a) {
        return $.grep(a, function(item, pos) {
            return $.inArray(item, a) === pos;
        });
  }

Das ist wiederum eine Variation des ersten Snippets.

Performance

Funktionsaufrufe sind in Javascript teuer, daher sind die obigen Lösungen, so prägnant sie auch sind, nicht besonders effizient. Für maximale Leistung ersetzen filter mit einer Schleife und loswerden von anderen Funktionsaufrufen:

function uniq_fast(a) {
    var seen = {};
    var out = [];
    var len = a.length;
    var j = 0;
    for(var i = 0; i < len; i++) {
         var item = a[i];
         if(seen[item] !== 1) {
               seen[item] = 1;
               out[j++] = item;
         }
    }
    return out;
}

Dieser Teil des hässlichen Codes tut das selbe wie der obige Ausschnitt # 3, aber eine Größenordnung schneller (ab 2017 ist es nur doppelt so schnell - JS Kern Leute machen einen tollen Job!)

function uniq(a) {
    var seen = {};
    return a.filter(function(item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}

function uniq_fast(a) {
    var seen = {};
    var out = [];
    var len = a.length;
    var j = 0;
    for(var i = 0; i < len; i++) {
         var item = a[i];
         if(seen[item] !== 1) {
               seen[item] = 1;
               out[j++] = item;
         }
    }
    return out;
}

/////

var r = [0,1,2,3,4,5,6,7,8,9],
    a = [],
    LEN = 1000,
    LOOPS = 1000;

while(LEN--)
    a = a.concat(r);

var d = new Date();
for(var i = 0; i < LOOPS; i++)
    uniq(a);
document.write('<br>uniq, ms/loop: ' + (new Date() - d)/LOOPS)

var d = new Date();
for(var i = 0; i < LOOPS; i++)
    uniq_fast(a);
document.write('<br>uniq_fast, ms/loop: ' + (new Date() - d)/LOOPS)

ES6

ES6 bietet die einstellen Objekt, das die Dinge viel einfacher macht:

function uniq(a) {
   return Array.from(new Set(a));
}

oder

let uniq = a => [...new Set(a)];

Beachten Sie, dass ES6-Sätze im Gegensatz zu Python in der Reihenfolge der Reihenfolge iteriert werden, sodass dieser Code die Reihenfolge des ursprünglichen Arrays beibehält.

Wenn Sie jedoch ein Array mit eindeutigen Elementen benötigen, können Sie Sets nicht von Anfang an verwenden.

Generatoren

Eine "faule", Generator-basierte Version von uniq kann auf der gleichen Basis gebaut werden:

  • nimm den nächsten Wert aus dem Argument
  • Wenn es schon gesehen wurde, überspringen Sie es
  • andernfalls ergebe es und füge es der Menge der bereits gesehenen Werte hinzu

function* uniqIter(a) {
    let seen = new Set();

    for (let x of a) {
        if (!seen.has(x)) {
            seen.add(x);
            yield x;
        }
    }
}

// example:

function* randomsBelow(limit) {
    while (1)
        yield Math.floor(Math.random() * limit);
}

// note that randomsBelow is endless

count = 20;
limit = 30;

for (let r of uniqIter(randomsBelow(limit))) {
    console.log(r);
    if (--count === 0)
        break
}

// exercise for the reader: what happens if we set `limit` less than `count` and why


2209
2018-02-10 15:05



Habe es satt, alle schlechten Beispiele mit for-loops oder jQuery zu sehen. Javascript hat dafür heute die perfekten Werkzeuge: Sortieren, Mappen und Reduzieren.

Uniq reduzieren unter Beibehaltung der bestehenden Reihenfolge

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

var uniq = names.reduce(function(a,b){
    if (a.indexOf(b) < 0 ) a.push(b);
    return a;
  },[]);

console.log(uniq, names) // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]

// one liner
return names.reduce(function(a,b){if(a.indexOf(b)<0)a.push(b);return a;},[]);

Schneller uniq mit Sortierung

Es gibt wahrscheinlich schnellere Wege, aber diese ist ziemlich anständig.

var uniq = names.slice() // slice makes copy of array before sorting it
  .sort(function(a,b){
    return a > b;
  })
  .reduce(function(a,b){
    if (a.slice(-1)[0] !== b) a.push(b); // slice(-1)[0] means last item in array without removing it (like .pop())
    return a;
  },[]); // this empty array becomes the starting value for a

// one liner
return names.slice().sort(function(a,b){return a > b}).reduce(function(a,b){if (a.slice(-1)[0] !== b) a.push(b);return a;},[]);

Update 2015: ES6-Version:

In ES6 gibt es Sets und Spreads, die es sehr einfach machen, alle Duplikate zu entfernen:

var uniq = [ ...new Set(names) ]; // [ 'Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Carl' ]

Nach Vorkommen sortieren:

Jemand fragte nach der Reihenfolge der Ergebnisse basierend darauf, wie viele eindeutige Namen es gibt:

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

var uniq = names
  .map((name) => {
    return {count: 1, name: name}
  })
  .reduce((a, b) => {
    a[b.name] = (a[b.name] || 0) + b.count
    return a
  }, {})

var sorted = Object.keys(uniq).sort((a, b) => uniq[a] < uniq[b])

console.log(sorted)

266
2018-04-07 22:42



Vanilla JS: Entferne Duplikate mit einem Objekt wie einem Set

Sie können immer versuchen, es in ein Objekt einzufügen und dann durch seine Schlüssel zu iterieren:

function remove_duplicates(arr) {
    var obj = {};
    var ret_arr = [];
    for (var i = 0; i < arr.length; i++) {
        obj[arr[i]] = true;
    }
    for (var key in obj) {
        ret_arr.push(key);
    }
    return ret_arr;
}

Vanilla JS: Duplikate entfernen, indem bereits gesehene Werte verfolgt werden (auftragsfrei)

Oder verwenden Sie für eine auftragssichere Version ein Objekt, um alle zuvor gesehenen Werte zu speichern, und überprüfen Sie Werte davor, bevor Sie sie zu einem Array hinzufügen.

function remove_duplicates_safe(arr) {
    var seen = {};
    var ret_arr = [];
    for (var i = 0; i < arr.length; i++) {
        if (!(arr[i] in seen)) {
            ret_arr.push(arr[i]);
            seen[arr[i]] = true;
        }
    }
    return ret_arr;

}

ECMAScript 6: Verwenden Sie die neue Datenstruktur Set (Order-Safe)

ECMAScript 6 fügt das neue hinzu Set Datenstruktur, in der Sie Werte beliebigen Typs speichern können. Set.values gibt Elemente in Anzeigenreihenfolge zurück.

function remove_duplicates_es6(arr) {
    let s = new Set(arr);
    let it = s.values();
    return Array.from(it);
}

Beispielverwendung:

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

b = remove_duplicates(a);
// b:
// ["Adam", "Carl", "Jenny", "Matt", "Mike", "Nancy"]

c = remove_duplicates_safe(a);
// c:
// ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]

d = remove_duplicates_es6(a);
// d:
// ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]

73
2018-02-10 15:03



Benutzen Underscore.js

Es ist eine Bibliothek mit einer Vielzahl von Funktionen zum Bearbeiten von Arrays.

Es ist die Krawatte, um mit jQuerys Tux und Backbone.js zu gehen   Hosenträger.

_.uniq

_.uniq(array, [isSorted], [iterator])  Alias:  einzigartig
  Erzeugt eine duplikatfreie Version des Arraymit === um das Objekt zu testen   Gleichberechtigung. Wenn Sie im Voraus wissen, dass die Array ist sortiert, vorbei    wahr zum istsortiert wird einen viel schnelleren Algorithmus ausführen. Wenn du möchtest   Berechne einzigartige Objekte basierend auf einer Transformation, passiere ein Iterator   Funktion.

Beispiel

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

alert(_.uniq(names, false));

Hinweis: Lo-Strich (ein unterstreichen Wettbewerber) bietet auch eine vergleichbare .uniq Implementierung.


68
2018-06-30 03:07



Eine einzeilige Version mit Array-Filter- und indexOf-Funktionen:

arr = arr.filter (function (value, index, array) { 
    return array.indexOf (value) == index;
});

60
2018-02-11 21:18



Sie können dies einfach in JavaScript tun, mit Hilfe des zweiten - Index - Parameters des filter Methode:

var a = [2,3,4,5,5,4];
a.filter(function(value, index){ return a.indexOf(value) == index });

oder kurz gesagt

a.filter((v,i) => a.indexOf(v) == i)

46
2018-06-15 11:05



Die einfachste Möglichkeit, Duplikate mit nativen JavaScript-Funktionen aus einem Array zu entfernen, besteht darin, eine Sequenz wie folgt zu verwenden:

vals.sort().reduce(function(a, b){ if (b != a[0]) a.unshift(b); return a }, [])

Es gibt keine Notwendigkeit dafür slice Noch indexOf innerhalb der Reduce-Funktion, wie ich es in anderen Beispielen gesehen habe! Es ist jedoch sinnvoll, es zusammen mit einer Filterfunktion zu verwenden:

vals.filter(function(v, i, a){ return i == a.indexOf(v) })

Eine weitere ES6 (2015) -Methode, die bereits auf einigen Browsern funktioniert, ist:

Array.from(new Set(vals))

oder sogar mit dem Spread-Operator:

[...new Set(vals)]

Prost!


28
2017-09-11 23:44



Eine Linie:

let names = ['Mike','Matt','Nancy','Adam','Jenny','Nancy','Carl', 'Nancy'];
let dup = [...new Set(names)];
console.log(dup);

28
2017-08-01 01:39



Gehen Sie für diesen einen:

var uniqueArray = duplicateArray.filter(function(elem, pos) {
    return duplicateArray.indexOf(elem) == pos;
}); 

Jetzt enthält uniqueArray keine Duplikate.


19
2018-02-27 09:53



Der Einfachste, dem ich bisher begegnet bin. In es6.

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

 var noDupe = Array.from(new Set(names))

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set


17
2017-12-25 05:18