Uzyskaj wszystkie unikalne wartości w tablicy JavaScript (usuń duplikaty)

1482

Mam szereg liczb, które muszę upewnić się, że są unikalne. Fragment kodu znalazłem poniżej w Internecie i działa świetnie, dopóki tablica nie będzie miała zera. Znalazłem ten inny skrypt tutaj na przepełnieniu stosu, który wygląda prawie dokładnie tak, ale nie zawodzi.

Czy więc, aby pomóc mi w nauce, ktoś może mi pomóc ustalić, gdzie źle działa prototypowy skrypt?

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

Więcej odpowiedzi z duplikatu pytania:

Podobne pytanie:

Mottie
źródło
3
@hippietrail To starsze pytanie dotyczy znalezienia i zwrotu tylko duplikatów (ja też byłem zdezorientowany!). Moje pytanie dotyczy bardziej tego, dlaczego ta funkcja zawodzi, gdy tablica ma zero.
Mottie
Prawdopodobnie chcesz też, aby tytuł pytania był mniej niejasny.
hippietrail
W przypadku przyszłych czytelników, gdy zaczniesz odkrywać, że musisz cały czas modyfikować algorytmicznie strukturę swojej struktury danych (porządkować je, usuwać powtarzające się elementy itp.) Lub wyszukiwać elementy wewnątrz niej przy każdej iteracji, możesz bezpiecznie założyć, że „Używam niewłaściwej struktury danych i zacznij używać takiej, która jest bardziej odpowiednia dla danego zadania (w tym przypadku zestaw skrótów zamiast tablicy).
nurettin
Skopiowałem kod gdzieś indziej, już dawno temu ... ale wydaje się dość prosty: o= object, a= array, i= indexi e= umm, coś: P
Mottie

Odpowiedzi:

2656

W JavaScript 1.6 / ECMAScript 5 możesz użyć natywnej filtermetody Array w następujący sposób, aby uzyskać tablicę o unikalnych wartościach:

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

Metoda natywna filterbędzie pętla w tablicy i pozostawi tylko te wpisy, które przejdą daną funkcję wywołania zwrotnego onlyUnique.

onlyUniquesprawdza, czy podana wartość występuje po raz pierwszy. Jeśli nie, musi być duplikatem i nie zostanie skopiowany.

To rozwiązanie działa bez dodatkowej biblioteki, takiej jak jQuery lub prototype.js.

Działa również w przypadku tablic o mieszanych typach wartości.

Dla starych przeglądarek (<IE9), które nie obsługują metod natywnych filteri indexOfmożna znaleźć arounds praca w dokumentacji MDN na filtrze i indexOf .

Jeśli chcesz zachować ostatnie wystąpienie wartości, po prostu zamień indexOfna lastIndexOf.

Z ES6 można to skrócić do tego:

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

Podziękowania dla Camilo Martina za podpowiedź w komentarzu.

ES6 ma obiekt macierzysty Setdo przechowywania unikalnych wartości. Aby uzyskać tablicę z unikalnymi wartościami, możesz teraz zrobić to:

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

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

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

Konstruktor Setpobiera iterowalny obiekt, taki jak Array, a operator rozkładania ...przekształca zestaw z powrotem w Array. Podziękowania dla Lukasa Liese za podpowiedź w komentarzu.

TLindig
źródło
53
To rozwiązanie będzie niestety działać znacznie wolniej. Zapętlasz dwa razy, raz z filtrem i raz z indeksem
Jack Franzen
18
@JackFranzen Wolniej niż co? Rozwiązanie od Rafaela ? Rozwiązanie Rafaels nie działa w przypadku tablic mieszanych. Za mój przykład ['a', 1, 'a', 2, '1']dostaniesz ['a', 1, 2]. Ale nie tego się spodziewałem. BTW, znacznie wolniejszy jest bardzo względny.
TLindig,
25
We współczesnym JS: .filter((v,i,a)=>a.indexOf(v)==i)(zapis grubej strzały).
Camilo Martin
162
let unique_values = [...new Set(random_array)]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Lukas Liesis,
5
Aby uzyskać bardziej szczegółową odpowiedź, w tym wiele możliwości - takich jak najpierw sortowanie i radzenie sobie z różnymi typami danych - patrz stackoverflow.com/a/9229821/368896
Dan Nissenbaum
873

Zaktualizowana odpowiedź dla ES6 / ES2015 : Przy użyciu zestawu rozwiązanie jednoliniowe to:

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

Który zwraca

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

Jak sugeruje le_m , można to również skrócić za pomocą operatora spread , np

var uniqueItems = [...new Set(items)]
W
źródło
10
Zauważ, że wewnętrzny układ nie działałbyArray.from(new Set([[1,2],[1,2],[1,2,3]]))
Alexander Goncharov
2
Wydajność tego rozwiązania w porównaniu do myArray.filter((v, i, a) => a.indexOf(v) === i);?
Simoyw,
42
Pamiętaj, że jeśli użyjesz Seti dodasz obiekty zamiast pierwotnych wartości, będą one zawierały unikalne odniesienia do obiektów. Zatem zestaw sw let s = new Set([{Foo:"Bar"}, {Foo:"Bar"}]);zwróci to: Set { { Foo: 'Bar' }, { Foo: 'Bar' } }co jest Setunikalnym odwołaniem do obiektu, który zawiera te same wartości. Jeśli napiszesz, let o = {Foo:"Bar"};a następnie utworzysz zestaw z dwoma odnośnikami : tak let s2 = new Set([o,o]);, wtedy s2 będzieSet { { Foo: 'Bar' } }
mortb
2
Obie te opcje mają problemy na IE10 nawet z warstwą polyfill.io obecną
tyminy
2
nowy przypadek testowy jsperf.com/array-filter-unique-vs-new-set/1 wydaje się new Settrofeum
shuk 13.01.19
167

Zdaję sobie sprawę, że to pytanie ma już ponad 30 odpowiedzi. Ale najpierw przeczytałem wszystkie istniejące odpowiedzi i przeprowadziłem własne badania.

Podzielę wszystkie odpowiedzi na 4 możliwe rozwiązania:

  1. Użyj nowej funkcji ES6: [...new Set( [1, 1, 2] )];
  2. Użyj obiektu, { }aby zapobiec duplikatom
  3. Użyj tablicy pomocniczej [ ]
  4. Posługiwać się filter + indexOf

Oto przykładowe kody znalezione w odpowiedziach:

Użyj nowej funkcji ES6: [...new Set( [1, 1, 2] )];

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

Użyj obiektu, { }aby zapobiec duplikatom

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

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

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

Użyj tablicy pomocniczej [ ]

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

Posługiwać się 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;
}

I zastanawiałem się, który z nich jest szybszy. Zrobiłem przykładowy Arkusz Google do testowania funkcji. Uwaga: ECMA 6 nie jest dostępny w Arkuszach Google, więc nie mogę go przetestować.

Oto wynik testów: wprowadź opis zdjęcia tutaj

Spodziewałem się, że kod wykorzystujący obiekt { }wygra, ponieważ używa skrótu. Cieszę się więc, że testy wykazały najlepsze wyniki dla tego algorytmu w Chrome i IE. Dzięki @rab za kod .

Max Makhrov
źródło
Opcja „filter + indexOf” jest bardzo wolna w przypadku tablic zawierających ponad 100 000 pozycji. Musiałem zastosować podejście „mapy obiektów”, ale psuje oryginalne sortowanie.
liberborn
Czy wyższa liczba jest wolniejsza, a nie szybsza?
fletchsod
3
@ fletchsod, liczby to czas w ms, aby uruchomić kod.
Max Makhrov,
1
Liczby są wong! Skrypt aplikacji Google działa na ich serwerze, a nie w przeglądarce klienta - dlatego wydajność w Chrome / IE (lub praktycznie każdej przeglądarce) będzie taka sama!
Jay Dadhania
1
Chociaż możliwe jest uruchomienie JS po stronie klienta z Google Script (albo przez google.script.host lub google.script.run - nie jestem pewien, który), odpowiedź wyraźnie stwierdza, że „ECMA 6 nie jest dostępny w Arkuszach Google” - dlatego możemy bezpiecznie założyć, że plakat użył do tego skryptu Google po stronie serwera - a zatem odpowiedź pokazuje wydajność serwera Google, a nie przeglądarki !!
Jay Dadhania
134

Możesz także użyć underscore.js .

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

który zwróci:

[1, 2, 3, 4]
kornfridge
źródło
22
Proszę, zróbcie to ludzie. Nie podłączaj niczego do prototypu Array. Proszę.
Jacob Dalton,
5
@JacobDalton - To nie jest rozszerzenie prototypu Array. Jest to przestrzeń nazw w obiekcie _.
superluminarny
24
@ superluminary Wiem, dlatego powiedziałem, proszę, zrób to. Zaakceptowane rozwiązanie sugeruje modyfikację prototypu Array. NIE rób tego.
Jacob Dalton
20
@JacobDalton Proszę nie rób tego. Nie ma potrzeby dodawania dodatkowej biblioteki tylko dla małego zadania, które można wykonaćarray = [...new Set(array)]
3
To zniweczyło cel używania funkcji ES i dodawania kolejnych bibliotek, które tylko powiększają witrynę.
fletchsod
68

One Liner, czysty JavaScript

Ze składnią 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")

wprowadź opis zdjęcia tutaj

Ze składnią ES5

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

Kompatybilność przeglądarki : IE9 +

Vamsi
źródło
13
@Spets Ma kwadratowy koszt, więc nie jest to najlepsza odpowiedź
Oriol
@Spets jak wszystkie unikalne / odrębne ... tak, bądź sprytny, użyj najpierw radix do sortowania, w większości przypadków wolniej niż szybkie wyszukiwanie 0 (n2).
doker
dzięki chłopaki! Nie zdawałem sobie z tego sprawy, kiedy po raz pierwszy spojrzałem na kod, ale teraz jest nieco bardziej oczywisty.
Spets
51

Od tego czasu znalazłem fajną metodę, która używa jQuery

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

Uwaga: ten kod został pobrany z kaczy Paula Irlandczyka - zapomniałem podać kredyt: P

Mottie
źródło
8
Zwięzłe rozwiązanie, ale wywołanie inArray jest znacznie mniej wydajne niż wywołanie hasOwnProperty.
Pan Smith
To także O (N ^ 2), prawda? Natomiast słownikiem lub hasOwnProperty prawdopodobnie będzie O (N * logN).
Speedplane
To działało dla mnie, wszystkie inne rozwiązania nie były obsługiwane przez Internet Explorer.
kerl
51

Najkrótsze rozwiązanie z ES6: [...new Set( [1, 1, 2] )];

Lub jeśli chcesz zmodyfikować prototyp Array (jak w pierwotnym pytaniu):

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

EcmaScript 6 jest obecnie tylko częściowo zaimplementowany w nowoczesnych przeglądarkach (sierpień 2015), ale Babel stał się bardzo popularny przy transpilowaniu ES6 (a nawet ES7) z powrotem do ES5. W ten sposób możesz napisać kod ES6 już dziś!

Jeśli zastanawiasz się, co to ...znaczy, nazywa się to operatorem rozprzestrzeniania . Z MDN : «Operator rozkładania pozwala na rozwinięcie wyrażenia w miejscach, w których oczekuje się wielu argumentów (dla wywołań funkcji) lub wielu elementów (dla literałów tablicowych)». Ponieważ zestaw jest iterowalny (i może mieć tylko unikalne wartości), operator rozkładania rozszerzy zestaw, aby wypełnić tablicę.

Materiały do ​​nauki ES6:

Torsten Becker
źródło
3
możesz to zrobić jeszcze krócej, a = [...Set(a)]ale na razie jest to tylko Firefox.
c69,
@ c69, racja, nie będzie krótszy niż to. Użytkownicy SpiderMonkey też to docenią.
Torsten Becker
Współpracuje z Babel. Musisz tylko dołączyć Array.frim shim również:require ( "core-js/fn/array/from" );
Vladislav Rastrusny
powinien to być Array.prototype.getUnique = function () {return [... new Set ([this])]; }; lub Array.prototype.getUnique = function () {return [... new Set (this)]; }; Zastosowałem to na następujących $ - (body) html (). ToLowerCase (). Match (/ ([a-zA-Z0-9 ._ + -] + @ [a-zA-Z0-9). _-] + \. [a-zA-Z0-9 ._-] +) / gi) .getUnique (); Pierwszy zwrócił mi tylko jedną unikalną wartość tylko wtedy, gdy drugi zrestartował wszystkie usuwające duplikaty.
ashique
[...Set(['a', 1, 'a', 2, '1'])]zgłosi błąd typu TypeError, więc nadal mądrze jest zachować new:[...new Set(['a', 1, 'a', 2, '1'])]
Adam Katz
36

Najprostsze rozwiązanie:

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

Lub:

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

Pedro L.
źródło
4
Pamiętaj, że nie jest to obsługiwane w IE10 i niższych wersjach. Pamiętaj, że IE11 + i Safari oferują ograniczone wsparcie (nawet nie jestem pewien, czy powyższy przykład działa) developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
oriadam
32

Najprostszy i najszybszy (w Chrome) sposób:

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

Po prostu przechodzi przez każdy element w tablicy, sprawdza, czy ten element jest już na liście, a jeśli nie, przesuń do tablicy, która zostanie zwrócona.

Według jsPerf ta funkcja jest najszybsza z tych, jakie mogłem znaleźć wszędzie - możesz jednak dodać własną.

Nieprototypowa wersja:

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

Sortowanie

Gdy trzeba również posortować tablicę, najszybsze są następujące czynności:

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

lub nieprototypowy:

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

Jest to również szybsze niż powyższa metoda w większości przeglądarek innych niż Chrome.

Joeytje50
źródło
W systemie Linux Chrome 55.0.2883 preferuje arr.unique (), a arrclone2.sortFilter () w swilliams jest najwolniejszy (78% wolniej). Jednak Firefox 51.0.0 (z dużą ilością dodatków) ma swilliamy najszybciej (jeszcze wolniej o Ops / s niż jakikolwiek inny wynik Chrome), a jQuery $ .grep (arr, jqFilter) Mottiego jest najwolniejszy (46% wolniej). Twoja arr.uniq () była o 30% wolniejsza. Przeprowadziłem każdy test dwa razy i uzyskałem spójne wyniki. Arr.getUnique () Rafaela zajął drugie miejsce w obu przeglądarkach.
Adam Katz
jsPerf jest buggy w tej chwili, więc moja edycja tego testu nie popełnić wszystko, ale to nie wynik na dodaniu dwóch testów: Cocco za toUnique () bije Vamsi za ES6 list.filter () po obu przeglądarek, pokonując sortFilter swilliams' () dla # 1 na FF (sortFilter był o 16% wolniejszy) i pokonanie posortowanego testu (który był wolniejszy o 2%) dla # 3 w Chrome.
Adam Katz
Ach, nie zauważyłem, że te testy były trywialnie małe i naprawdę nie mają znaczenia. Komentarz do zaakceptowanej odpowiedzi opisuje ten problem i oferuje korektę w wersji testu, w której kod Rafaela jest z łatwością najszybszy, a kod arr.unique Joetje50 jest o 98% wolniejszy. Dokonałem również kolejnej zmiany, jak zauważono w tym komentarzu .
Adam Katz
1
Cóż, właściwie algorytm zaimplementowany w uniquefunkcji ma złożoność O (n ^ 2), podczas gdy ten w getUniqueto O (n). Pierwszy może być szybszy na małych zestawach danych, ale jak możesz się kłócić z matematyką :) Możesz upewnić się, że ten drugi jest szybszy, jeśli uruchomisz go na szeregu, powiedzmy, 1e5 unikalnych przedmiotów
Mikhail Dudin
31

TYLKO WYDAJNOŚĆ! ten kod jest prawdopodobnie 10 razy szybszy niż wszystkie kody tutaj * działa na wszystkich przeglądarkach, a także ma najmniejszy wpływ na pamięć .... i więcej

jeśli nie musisz ponownie używać starej tablicy; btw wykonaj niezbędne operacje, zanim skonwertujesz ją na unikalną, jest to prawdopodobnie najszybszy sposób na zrobienie tego, również bardzo krótki.

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

to możesz spróbować

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]

Wymyśliłem tę funkcję, czytając ten artykuł ...

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

Nie lubię pętli for. ma wiele parametrów. lubię pętlę while--. podczas gdy jest najszybszą pętlą we wszystkich przeglądarkach oprócz tej, którą wszyscy bardzo lubimy ... chrome.

w każdym razie napisałem pierwszą funkcję, która używa while. Tak, jest to trochę szybsze niż funkcja znaleziona w artykule. ale za mało.unique2()

w następnym kroku użyj nowoczesnego js. Object.keys drugą pętlę for zastąpiłem Object.keys js1.7 ... trochę szybciej i krócej (w chrome 2x szybciej);). Niewystarczająco!. unique3().

w tym momencie myślałem o tym, czego naprawdę potrzebuję w MOJEJ unikalnej funkcji. nie potrzebuję starej tablicy, chcę szybkiej funkcji. więc użyłem 2 pętli while + splot.unique4()

Nie trzeba dodawać, że byłem pod wrażeniem.

chrome: zwykłe 150 000 operacji na sekundę skoczyło do 1 800 000 operacji na sekundę.

tj. 80 000 operacji na sekundę w porównaniu do 35 000 000 operacji na sekundę

ios: 18 000 operacji na sekundę vs 170 000 operacji na sekundę

safari: 80 000 operacji na sekundę vs 6 000 000 operacji na sekundę

Dowód http://jsperf.com/wgu lub lepiej użyj console.time ... microtime ... cokolwiek

unique5() jest po prostu pokazanie, co się stanie, jeśli chcesz zachować starą tablicę.

Nie używaj, Array.prototypejeśli nie wiesz, co robisz. właśnie zrobiłem dużo kopii i przeszłości. Użyj, Object.defineProperty(Array.prototype,...,writable:false,enumerable:false})jeśli chcesz utworzyć natywny prototyp. Przykład : https://stackoverflow.com/a/20463021/2450730

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

UWAGA: twoja stara tablica jest niszczona / staje się unikalna po tej operacji.

jeśli nie możesz odczytać powyższego kodu, zapytaj, przeczytaj książkę javascript lub oto wyjaśnienia dotyczące krótszego kodu. https://stackoverflow.com/a/21353032/2450730

niektórzy używają indexOf ... nie ... http://jsperf.com/dgfgghfghfghghgfhgfhfghfhgfh

dla pustych tablic

!array.length||toUnique(array); 
cocco
źródło
4
testowane na node.js, z 100k tablicą adresów URL (ciągów). Wynik był 2x wolniej niż underscore.js _.uniq ... chociaż oddzielna jsperf zgadza się z wami ( jsperf.com/uniq-performance/5 ), jestem rozczarowany :(
xShirase
3
nie testujesz poprawnie w jsperf ... w twoim przykładzie za każdym razem definiujesz funkcję ... ale funkcje underscore.js są już zdefiniowane .. to penalizuje moją funkcję. również przetestuj 3 i 4. inną rzeczą, którą powinienem powiedzieć, jest to, że jeśli używasz zmiennych mieszanych (ciągów i liczb), powinieneś zamienić [b]! == a [c] na [b]! = a [c]
cocco
2
Nie jestem pewien, czy jsPerf poprawił, ale wydaje się, że alternatywa Reduce (to dla mnie jest łatwiejsze do zrozumienia) jest nieco 94% szybsza niż twoje rozwiązanie. jsperf.com/reduce-for-distinct Edycja: Twoja jest wolniejsza na Chrome, to samo na Edge i szybsza na Firefox.
Victor Ivens
3
Można używać tylko z małymi tablicami / małymi duplikatami . Za każdym razem, gdy zostanie znaleziony nieunikalny obiekt, silnik będzie zmuszony przesunąć wszystkie pozostałe elementy w lewo, poprzez: 1) iterację nad nimi 2) odczytanie ich 3) zapisanie ich w nowej lokalizacji. A potem, to również tworzy nową tablicę z tego jednego elementu łączone i zwraca ją jako wynik ... Algorytm szybko degradują z większych tablic / częstszych duplikatów. W przypadku tablic o rozmiarach 1000-> 10000-> 50000 i ~ 40% duplikatów średni czas wykonania wyniesie 2-> 775-> 19113 ms. (rozmiar zmienia się jako 1-> x10-> x5, czas jako 1-> x10-> x25) w Chrome.
ankhzet
1
Dzięki. Niezłe podejście. Ale co z prawidłowym zamówieniem przedmiotów?
liberborn
28

Wiele odpowiedzi tutaj może nie być przydatnych dla początkujących. Jeśli odkodowanie tablicy jest trudne, czy naprawdę będą wiedzieć o łańcuchu prototypów, a nawet o jQuery?

W nowoczesnych przeglądarkach czystym i prostym rozwiązaniem jest przechowywanie danych w zestawie , który ma być listą unikalnych wartości.

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

Array.fromJest przydatna do konwersji cofnięty do tablicy tak, że masz łatwy dostęp do wszystkich wspaniałych metod (cechy), które mają tablice. Istnieją również inne sposoby robienia tego samego. Ale może nie być wcale potrzebny Array.from, ponieważ zestawy mają wiele przydatnych funkcji, takich jak forEach .

Jeśli potrzebujesz obsługi starego programu Internet Explorer, a zatem nie możesz użyć zestawu, prostą techniką jest skopiowanie elementów do nowej tablicy, uprzednio sprawdzając, czy są już w nowej tablicy.

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

Aby umożliwić natychmiastowe ponowne użycie, włączmy funkcję.

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

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

        return result;
    }
}

Aby pozbyć się duplikatów, zrobilibyśmy to teraz.

var uniqueCars = deduplicate(cars);

deduplicate(cars)Część staje się rzeczą nazwaliśmy wynik gdy finalizuje funkcyjnych.

Po prostu podaj nazwę dowolnej tablicy, którą lubisz.

Seth Holladay
źródło
Nawiasem mówiąc, użyłem tablicy pełnej ciągów, aby pokazać, że moja technika jest elastyczna. Będzie działać poprawnie dla liczb.
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;
 }, []);
sergeyz
źródło
Czy możesz wyjaśnić, dlaczego nie użyłeś pushelementu na tablicy zamiast go używać concat? Próbowałem użyć wypychania i nie udało się. Szukam wyjaśnienia.
makenova
1
Powodem jest wartość zwracana. concat zwraca zmodyfikowaną tablicę (dokładnie to, co należy zwrócić w funkcji zmniejszania), a push zwraca indeks, przy którym można uzyskać dostęp do wartości wypychanej. Czy to jest odpowiedź na Twoje pytanie?
sergeyz
Najwyraźniej to rozwiązanie jest dość szybkie w chrome (i node). jsperf.com/reduce-for-distinct To jest wersja ES6: [0,1,2,0,3,2,1,5].reduce((prev, cur) => ~prev.indexOf(cur) ? prev : prev.concat([cur]), []);
Victor Ivens
uwaga dodatkowa: ta implementacja nie jest NaNprzyjazna
Alireza
19

Możemy to zrobić za pomocą zestawów ES6:

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

console.log(uniqueArray);

// Wyjście będzie

uniqueArray = [1,2,3,4,5];
chinmayan
źródło
17

Ten prototyp getUniquenie jest całkowicie poprawny, ponieważ jeśli mam tablicę typu: ["1",1,2,3,4,1,"foo"]zwróci ["1","2","3","4"]i "1"jest ciągiem i 1jest liczbą całkowitą; oni są różni.

Oto prawidłowe rozwiązanie:

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

za pomocą:

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

Powyższe spowoduje ["1",2,3,4,1,"foo"].

Gabriel Silveira
źródło
1
Zauważ, że $foo = 'bar'jest to sposób deklarowania zmiennych w PHP. Będzie działał w javascript, ale stworzy domyślną globalną i na ogół nie powinno się tego robić.
Camilo Martin
@CamiloMartin przepraszam, ale się mylisz, $ foo ma charakter globalny, ponieważ przykład nie jest zamknięty i brakuje słowa kluczowego var. Nie ma nic wspólnego z dolarem jsfiddle.net/robaldred/L2MRb
Rob
8
@Rob dokładnie to, co mówię, ludzie PHP pomyślą, że $foojest to sposób deklarowania zmiennych w javascript, podczas gdy tak naprawdę var foojest.
Camilo Martin
13

Bez rozszerzenia Array.prototype (mówi się, że jest to zła praktyka) lub użycia jquery / podkreślenia, możesz po prostu filtertablicę.

Zachowując ostatnie wystąpienie:

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

lub pierwsze wystąpienie:

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

Cóż, to tylko javascript ECMAScript 5+, co oznacza tylko IE9 +, ale jest przyjemny dla rozwoju w natywnym HTML / JS (aplikacja Windows Store, Firefox OS, Sencha, Phonegap, Titanium, ...).

Cœur
źródło
2
Fakt, że jest to js 1.6, nie oznacza, że ​​nie możesz go używać filter. Na stronie MDN mają implementację dla Internet Explorera, to znaczy starszych przeglądarek. Ponadto: JS 1.6 odnosi się tylko do silnika js Firefoksa, ale należy powiedzieć, że jest to ECMAScript 5.
Camilo Martin
@CamiloMartin Zmieniłem 1.6 na ECMAScript5. Dzięki.
Cœur
13

magia

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

O (n) wydajność ; zakładamy, że twoja tablica jest w ai t={}. Objaśnienie tutaj (+ Jeppe impr.)

Kamil Kiełczewski
źródło
14
ten wygląd jest tak fajny, że bez solidnego wyjaśnienia wpadłem, że
wydobędziesz bitcoiny,
3
miałem na myśli to, że powinieneś poszerzyć swoją odpowiedź o wyjaśnienia i skomentować jej dekonstrukcję. nie oczekuj, że ludzie znajdą takie przydatne odpowiedzi. (choć naprawdę wygląda fajnie, prawdopodobnie działa)
Ondřej Želazko
1
@Jeppe, kiedy uruchamiam twoje ulepszenie, wtedy odczuwam efekt aha (zanim nie wiem, że mogę używać inoperatora poza konstrukcją inną niż forpętla: P) - Dziękuję - doceniam to i dam +2 do twoich innych dobrych odpowiedzi .
Kamil Kiełczewski
2
czy to nie tworzy globalnej zmiennej, tktóra utrzymuje się przy życiu po filtrowaniu… ??
philipp
1
To naprawdę trudne rozwiązanie. Niestety musisz zadeklarować t poza wyrażeniem, chyba że chcesz zdefiniować zmienną globalną i / lub polegać na pakiecie modułów, aby ukryć zmienną globalną. Wygląda fantazyjnie, ale zrób sobie (i swojemu zespołowi programistów) przysługę i po prostu napisz dwie linijki: let t = {}; a. filtr (e =>! (t [e] = e wt))
ford04
13
[...new Set(duplicates)]

Jest to najprostszy i do którego odwołuje się dokument 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]
ifelse.codes
źródło
Chociaż ten kod może rozwiązać pytanie, w tym wyjaśnienie, w jaki sposób i dlaczego to rozwiązuje problem, naprawdę pomógłby poprawić jakość twojego postu i prawdopodobnie doprowadziłby do większej liczby głosów. Pamiętaj, że odpowiadasz na pytanie dla czytelników w przyszłości, nie tylko dla osoby zadającej teraz pytanie. Edytuj swoją odpowiedź, aby dodać wyjaśnienie i podać, jakie ograniczenia i założenia mają zastosowanie.
id.ot
11

Jeśli używasz Prototype Framework, nie musisz wykonywać pętli „for”, możesz użyć http://www.prototypejs.org/api/array/uniq w następujący sposób:

var a = Array.uniq();  

Który wytworzy duplikat tablicy bez duplikatów. Natknąłem się na twoje pytanie, szukając metody zliczania różnych rekordów tablic, więc potem

uniq ()

użyłem

rozmiar()

i był mój prosty wynik. ps Przepraszam, jeśli coś źle wpisałem

edycja: jeśli chcesz uciec niezdefiniowanym rekordom, możesz je dodać

kompaktowy()

wcześniej, tak:

var a = Array.compact().uniq();  
Decebal
źródło
14
ponieważ znalazłem lepszą odpowiedź, myślę, że tematy są dla wszystkich ludzi, nie tylko dla tego, który zapytał
Decebal
11

Teraz za pomocą zestawów możesz usunąć duplikaty i przekonwertować je z powrotem do tablicy.

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

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

Innym rozwiązaniem jest użycie sortowania i filtrowania

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

Krishnadas PC
źródło
10

To dlatego, że 0jest to wartość fałsz w JavaScript.

this[i] będzie fałszem, jeśli wartość tablicy wynosi 0 lub dowolna inna wartość fałszowania.

Luca Matteis
źródło
Ahhhh, ok, rozumiem teraz ... ale czy będzie łatwa poprawka, żeby to zadziałało?
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
}
efemeryczny
źródło
Myślę, że to nie zadziała, jeśli tablica zawiera obiekty / tablice i nie jestem pewien, czy zachowa rodzaj skalarów.
Camilo Martin
Tak, wszystko zostaje sprecyzowane. Można to naprawić, przechowując oryginalną wartość ozamiast po prostu 1, chociaż porównanie równości nadal byłoby rygorystyczne (chociaż ze wszystkich możliwych równości Javascript nie wydaje się to zbyt nierozsądne).
ephemient
Prototyp Array.prototype można rozszerzyć tylko metodami niepoliczalnymi ... Object.defineProperty (Array.prototype, „getUnique”, {}) ... ale pomysł użycia obiektu pomocnika jest bardzo fajny
bortunac,
10

Miałem nieco inny problem, w którym musiałem usunąć obiekty z duplikatami właściwości id z tablicy. to zadziałało.

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

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

console.log(objArr);

shunryu111
źródło
9

Najprostsza odpowiedź to:

const array = [1, 1, 2, 2, 3, 5, 5, 2];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // [1, 2, 3, 5]
Sukanta Bala
źródło
Proste, ale nie działa z szeregiem obiektów.
Thanwa Ch.
7

Nie jestem pewien, dlaczego Gabriel Silveira napisał tę funkcję w ten sposób, ale prostsza forma, która działa dla mnie równie dobrze i bez minimalizacji, to:

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

lub w CoffeeScript:

Array.prototype.unique = ->
  this.filter( (value, index, array) ->
    array.indexOf(value, index + 1) < 0
  )
Dan Fox
źródło
7

Jeśli nie masz nic przeciwko dodatkowym zależnościom lub masz już jedną z bibliotek w bazie kodu, możesz usunąć duplikaty z tablicy za pomocą LoDash (lub Underscore).

Stosowanie

Jeśli nie masz go jeszcze w swojej bazie kodu, zainstaluj go za pomocą npm:

npm install lodash

Następnie użyj go w następujący sposób:

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

Na zewnątrz:

[ 1, 2, 3 ]
NikeshPathania
źródło
7

Odpowiedzi udzielono na wiele, ale nie zaspokoiło to mojej szczególnej potrzeby.

Wiele odpowiedzi jest takich:

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

Ale to nie działa w przypadku tablic złożonych obiektów.

Powiedzmy, że mamy taką tablicę:

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

Jeśli chcemy obiektów o unikalnych nazwach, powinniśmy użyć array.prototype.findIndexzamiast array.prototype.indexOf:

a.filter((item, pos, self) => self.findIndex(v => v.name === item.name) === pos);
Dave
źródło
Świetne rozwiązanie, strzeż się, że nowa funkcja zwróci funkcję. (sam się nie modyfikuje)
Thanwa Ch.
6

Od Shamasis Bhattacharya „s blog (O (2n) czasu złożoności):

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

Z bloga Paula Irisha : poprawa 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]
Frosty Z
źródło
6

Znajdowanie unikalnych wartości tablic w prosty sposób

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]
Saravanan Rajaraman
źródło
6

Wygląda na to, że utraciliśmy odpowiedź Rafaela , która przez kilka lat stała się odpowiedzią przyjętą. Było to (przynajmniej w 2017 r.) Najskuteczniejsze rozwiązanie, jeśli nie masz tablicy typu mieszanego :

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

Jeśli zrobić posiada tablicę mieszany typu można szeregować klawisz skrótu:

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;
}
Jason
źródło
5

Aby rozwiązać ten problem na odwrót, przydatne może być brak duplikatu podczas ładowania tablicy, tak jak zrobiłby to obiekt Set , ale nie jest jeszcze dostępny we wszystkich przeglądarkach. Oszczędza pamięć i jest bardziej wydajna, jeśli trzeba wielokrotnie przeglądać jej zawartość.

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

Próba:

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

Daje Ci set = [1,3,4,2]

Le Droid
źródło