Bir JavaScript dizisindeki tüm benzersiz değerleri alın (kopyaları kaldırın)


1485

Benzersiz olduğundan emin olmak için gereken bir dizi sayı var. Aşağıdaki kod snippet'i internette buldum ve dizi içinde sıfır olana kadar harika çalışıyor. Bu diğer betiği neredeyse tamamen benzeyen Stack Overflow'da buldum , ancak başarısız değil.

Öyleyse öğrenmeme yardım etmek adına, biri prototip betiğinin nerede yanlış gittiğini belirlememe yardımcı olabilir mi?

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

Yinelenen sorudan daha fazla yanıt:

Benzer soru:


3
@hippietrail Bu eski soru sadece kopyaları bulmak ve iade etmektir (Ben de kafam karıştı!). Benim sorum bir dizi içinde sıfır olduğunda bu işlevin neden başarısız olduğu hakkında daha fazla.
Mottie

Muhtemelen soru başlığınızı daha az belirsiz yapmak istersiniz.
hippietrail

Gelecekteki okuyucular için, veri yapınızın içeriğini her zaman algoritmik olarak değiştirmeniz gerektiğini (bunları sipariş edin, yinelenen öğeleri kaldırın, vb.) Veya her yinelemede içindeki öğeleri aramak zorunda olduğunuzu bulmaya başladığınızda, İlk etapta yanlış veri yapısını kullanıyorsunuz ve eldeki görev için daha uygun olanı kullanmaya başlıyorsunuz (bu durumda dizi yerine bir karma kümesi).
nurettin

Kodu başka bir yerden kopyaladım, bir zaman önce ... ama oldukça basit görünüyor: o= object, a= array, i= indexve e= umm, bir şey: P
Mottie

Yanıtlar:


2660

İle JavaScript 1.6 / ECMAScript 5 Eğer yerli kullanabilirsiniz filterbenzersiz değerlere sahip bir dizi almak için şu şekilde bir Array yöntemini:

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

Yerel yöntem filterdizi içinde döngü yapar ve yalnızca belirli geri çağrı işlevini geçen girdileri bırakır onlyUnique.

onlyUniqueverilen değerin ilk gerçekleşip gerçekleşmediğini kontrol eder. Değilse, kopya olmalı ve kopyalanmayacaktır.

Bu çözüm, jQuery veya prototype.js gibi ek bir kitaplık olmadan çalışır.

Karışık değer türlerine sahip diziler için de çalışır.

Eski tarayıcılar için (<IE9), o doğal yöntemleri desteklemez filterve indexOfsizin için MDN belgelerinde çalışma arounds bulabilirsiniz filtresi ve indexOf .

Eğer bir değerin son oluşumunu korumak istiyorsanız, basit değiştirin indexOftarafından lastIndexOf.

ES6 ile bu kısaltılabilir:

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

Yorumdaki ipucu için Camilo Martin'e teşekkürler .

ES6'nın Setbenzersiz değerleri saklamak için yerel bir nesnesi vardır . Benzersiz değerlere sahip bir dizi elde etmek için şimdi bunu yapabilirsiniz:

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

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

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

Yapıcı, SetArray gibi yinelenebilir bir nesneyi alır ve forma operatörü ...seti tekrar Array'a dönüştürür. Yorumdaki ipucu için Lukas Liese'ye teşekkürler .


53
Bu çözüm maalesef çok daha yavaş çalışacaktır. Bir kez filtre ile ve bir kez de indeksle iki kez döngü yapıyorsunuz
Jack Franzen 23.11.2013

18
@JackFranzen Ne daha yavaş? Rafael'in çözümü ? Rafaels çözümü karışık tip dizilerde çalışmaz. Benim örneğim ['a', 1, 'a', 2, '1']için alırsınız ['a', 1, 2]. Ama beklediğim bu değil. BTW, çok daha yavaş çok göreceli.
TLindig

25
Modern JS'de: .filter((v,i,a)=>a.indexOf(v)==i)(şişman ok gösterimi).
Camilo Martin

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

5
Önce sıralama ve değişen veri türleriyle uğraşma gibi birçok olasılık dahil çok daha ayrıntılı bir yanıt için bkz. Stackoverflow.com/a/9229821/368896
Dan Nissenbaum

874

ES6 / ES2015 için cevap Güncelleme : kullanma Set , tek satır çözümdür:

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

Hangi döndürür

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

Gibi le_m önerdi, bu da kullanılarak kısaltılabilir yayılmış operatörü gibi,

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

10
Dikkat edin, bu iç dizi işe yaramazArray.from(new Set([[1,2],[1,2],[1,2,3]]))
Alexander Goncharov

2
Bu çözümün performansı ile karşılaştırıldığında myArray.filter((v, i, a) => a.indexOf(v) === i);?
Simoyw

42
Setİlkel değerler yerine ve öğeleri eklerseniz, nesnelerin benzersiz referansları içereceğini lütfen unutmayın . Böylece resim solarak let s = new Set([{Foo:"Bar"}, {Foo:"Bar"}]);: Bu döner Set { { Foo: 'Bar' }, { Foo: 'Bar' } }bir olan Setaynı değerleri içeren nesnelere benzersiz nesne referanslarla. Eğer yazarsanız let o = {Foo:"Bar"};ve iki ile daha sonra bir dizi oluşturmak referanslar bu yüzden istiyorum: let s2 = new Set([o,o]);daha sonra s2 olacakSet { { Foo: 'Bar' } }
mortb

2
Bu seçeneklerin her ikisi de bir polyfill.io katmanı olsa bile IE10'da sorun yaşıyor
Timin

2
Yeni test case jsperf.com/array-filter-unique-vs-new-set/1 gibi görünüyor new Setbireyin kupa
shuk

167

Bu sorunun zaten 30'dan fazla cevabı olduğunu biliyorum. Ama önce mevcut tüm cevapları okudum ve kendi araştırmamı yaptım.

Tüm cevapları 4 olası çözüme böldüm:

  1. Yeni ES6 özelliğini kullanın: [...new Set( [1, 1, 2] )];
  2. { }Yinelemeleri önlemek için nesne kullanma
  3. Yardımcı dizi kullan [ ]
  4. kullanım filter + indexOf

Yanıtlarda bulunan örnek kodlar şunlardır:

Yeni ES6 özelliğini kullanın: [...new Set( [1, 1, 2] )];

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

{ }Yinelemeleri önlemek için nesne kullanma

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

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

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

Yardımcı dizi kullan [ ]

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

kullanım 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;
}

Hangisinin daha hızlı olduğunu merak ettim. Yaptığım Örnek Google Sheet testi işlevlerine. Not: ECMA 6, Google E-Tablolar'da mevcut değildir, bu yüzden test edemiyorum.

İşte testlerin sonucu: resim açıklamasını buraya girin

{ }Hash kullandığı için nesne kullanarak kod kazanacağını görmeyi bekledim . Testlerin Chrome ve IE'de bu algoritma için en iyi sonuçları gösterdiğine sevindim. @Rab kodu için teşekkürler .


100.000 öğenin üzerindeki dizilerde "filter + indexOf" seçeneği son derece yavaştır. "Nesne haritası" yaklaşımını kullanmak zorunda kaldım ancak orijinal sıralamayı bozdu.
liberborn

Daha yüksek sayı daha yavaş değil, daha yavaş değil mi?
32'de fletchsod

3
@ fletchsod, sayılar kodu çalıştırma zamanı ms'dir.
Max Makhrov

1
Sayılar wong! Google apps komut dosyası, istemcinin tarayıcısında değil, sunucularında çalışır ve bu nedenle Chrome / IE'deki (veya neredeyse tüm tarayıcılardaki) performans aynı olacaktır!
Jay Dadhania

1
İken mümkün (ya aracılığıyla Google Script gelen istemci tarafı JS çalıştırmak için google.script.host veya google.script.run - emin olan), cevap açıkça belirtmektedir "ECMA 6 Google E kullanılabilir olmadığını da" - bu nedenle posterin bunun için sunucu tarafı Google Script kullandığını varsayabiliriz ve böylece yanıt, tarayıcının değil Google Sunucusunun performansını gösterir!
Jay Dadhania

134

Underscore.js dosyasını da kullanabilirsiniz .

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

geri dönecek:

[1, 2, 3, 4]

22
Lütfen bunu yap. Array prototipine bir şey takmayın. Lütfen.
Jacob Dalton

5
@JacobDalton - Bu, Array prototipini genişletmez. _ Nesnesinin adında boşluk bırakılmıştır.
superluminary

24
@superluminary biliyorum bu yüzden lütfen bunu yap dedim . Kabul edilen çözüm, Array prototipinin değiştirilmesini önerir. Bunu yapma.
Jacob Dalton

20
@JacobDalton Lütfen bunu yapma. Sadece yapılabilecek küçük bir iş için ekstra bir kütüphane eklemeye gerek yokturarray = [...new Set(array)]

3
Bu, ES özelliklerini kullanma ve daha fazla kütüphane ekleme amacını sadece web sitesini daha fazla şişirir.
fletchsod

68

Bir Astar, Saf JavaScript

ES6 sözdizimi ile

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

resim açıklamasını buraya girin

ES5 sözdizimi ile

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

Tarayıcı Uyumluluğu : IE9 +


13
@Spets İkinci dereceden maliyeti var, bu yüzden en iyi cevap değil
Oriol

@ Tüm benzersizler / farklılıklar gibi ... Evet, akıllı ol, önce sıralamak için sayı tabanı kullan, çoğu durumda 0 (n2) hızlı aramadan daha yavaş ol.
doker

teşekkürler beyler! İlk koda baktığımda fark etmedi ama şimdi biraz daha açık.
Spets

51

O zamandan beri jQuery kullanan güzel bir yöntem buldum

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

Not: Bu kod Paul Irish'ın ördek delme direğinden alınmıştır - Kredi vermeyi unuttum: P


8
Kısa bir çözüm, ancak inArray'ı çağırmak hasOwnProperty'yi çağırmaktan çok daha az verimlidir.
Bay Smith

Bu da O (N ^ 2), değil mi? Buna karşılık sözlük veya hasOwnProperty yaklaşımı muhtemelen O (N * logN) olur.
speedplane

Bu benim için çalıştı, diğer tüm çözümler Internet Explorer olarak desteklenmedi.
kerl

51

ES6 ile en kısa çözüm: [...new Set( [1, 1, 2] )];

Veya Array prototipini değiştirmek istiyorsanız (orijinal sorudaki gibi):

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

EcmaScript 6 şu anda modern tarayıcılarda sadece kısmen uygulanmaktadır (Ağustos 2015), ancak Babel ES6'yı (ve hatta ES7'yi) ES5'e geri aktarmak için çok popüler oldu. Bu şekilde bugün ES6 kodunu yazabilirsiniz!

Bunun ne ...anlama geldiğini merak ediyorsanız , buna yayılma operatörü denir . Gönderen MDN'yi : «yayılmış operatörü bir ifade ya (dizi sayılların) çoklu elemanlar (işlev çağrıları için) birden argümanlar beklenen yerlerde genişletilmiş sağlar». Bir Set yinelenebilir (ve yalnızca benzersiz değerlere sahip olabileceğinden) olduğundan, forma operatörü diziyi doldurmak için Set'i genişletir.

ES6 öğrenmek için kaynaklar:


3
bunu daha da kısa yapabilirsiniz a = [...Set(a)], ancak, her neyse, bu şimdilik sadece Firefox.
c69

@ c69, doğru, bundan daha kısa olmayacak. SpiderMonkey kullanıcıları da takdir edecektir.
Torsten Becker

Babel ile çalışır. Sadece Array.from shim'i de eklemeniz gerekir:require ( "core-js/fn/array/from" );
Vladislav Rastrusny

Array.prototype.getUnique = function () {return [... new Set ([this])]; }; veya, Array.prototype.getUnique = function () {return [... new Set (this)]; }; Aşağıdaki- $ ('body'). Html (). ToLowerCase (). Match (/ ([a-zA-Z0-9 ._ + -] + @ [a-zA-Z0-9 üzerine uyguladım. . _-] + \ [a-z-0-9 ._-] +) / ul) .getUnique (); Birincisi bana sadece benzersiz bir değer döndürdü, ikincisi tüm yinelenen kopyaları geri aldı.
Ocak'ta ashique

[...Set(['a', 1, 'a', 2, '1'])]bir TypeError atar, bu yüzden hala saklamak akıllıca olacaktır new:[...new Set(['a', 1, 'a', 2, '1'])]
Adam Katz

36

En basit çözüm:

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

Veya:

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


4
Bunun IE10 ve önceki sürümlerde desteklenmediğini unutmayın. IE11 + ve Safari'nin sınırlı destek sunduğunu unutmayın (yukarıdaki örneğin işe yaradığından emin değilim) developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
oriadam

32

Bunu yapmanın en basit ve en hızlı (Chrome'da) yolu:

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

Basitçe dizideki her öğeyi geçer, o öğenin listede olup olmadığını test eder ve değilse, döndürülen diziye itin.

JsPerf'e göre, bu işlev her yerde bulabildiğim en hızlı işlevdir - kendinizinkini eklemekten çekinmeyin.

Prototip olmayan sürüm:

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

sınıflandırma

Diziyi sıralamak gerektiğinde, aşağıdakiler en hızlısıdır:

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

veya prototip olmayan:

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

Bu aynı zamanda çoğu krom olmayan tarayıcıda yukarıdaki yöntemden daha hızlıdır .


Linux'ta Chrome 55.0.2883, arr.unique () öğenizi tercih eder ve swilliams'ın arrclone2.sortFilter () en yavaş (% 78 daha yavaş). Bununla birlikte, Firefox 51.0.0 (çok sayıda eklentiyle birlikte) mottie'nin jQuery $ .grep (arr, jqFilter) en yavaş (% 46 daha yavaş ) olmak üzere en hızlı (ancak Ops / sn tarafından diğer tüm Chrome sonuçlarından daha yavaş) swilliam'lara sahiptir . Arr.uniq () öğeniz% 30 daha yavaştı. Her testi iki kez yaptım ve tutarlı sonuçlar elde ettim. Rafael'in arr.getUnique () yöntemi her iki tarayıcıda da ikinci oldu.
Adam Katz

jsPerf şu anda buggy , bu yüzden bu testte yaptığım düzenleme her şeyi yapmadı, ancak iki test eklemeye neden oldu: Cocco's toUnique () , Vamsi'nin ES6 list.filter () öğesini her iki tarayıcıda da atıyor, swilliams sortFilter () FF'de # 1 için (sortFilter% 16 daha yavaştı) ve Chrome'da 3 numara için sıralı testinizi (% 2 daha yavaş) geçiyor.
Adam Katz

Ah, bu testlerin önemsiz derecede küçük olduğunu ve gerçekten önemli olmadığını yakalamamıştım. Kabul edilen cevaba yapılan bir yorum , sorunu açıklar ve testin gözden geçirilmesinde bir düzeltme sunar ; Rafael'in kodunun kolayca en hızlı olduğu ve Joetje50'nin arr. Benzersiz kodunun% 98 daha yavaş olduğu. Ayrıca bu yorumda belirtildiği gibi bir düzeltme daha yaptım .
Adam Katz

1
Aslında, uniquefonksiyonda uyguladığınız algoritma O (n ^ 2) karmaşıklığına sahipken, içerideki algoritma getUniqueO (n). İlki küçük veri kümelerinde daha hızlı olabilir, ancak matematikle nasıl tartışabilirsiniz :) 1e5 benzersiz öğeden oluşan bir dizi üzerinde çalıştırırsanız, ikincisinin daha hızlı olduğundan emin olabilirsiniz
Mikhail Dudin

31

YALNIZCA PERFORMANS! Bu kod muhtemelen burada tüm kodlardan 10 kat daha hızlı * tüm tarayıcılarda çalışır ve aynı zamanda en düşük bellek etkisi vardır .... ve daha fazlası

eski diziyi yeniden kullanmanız gerekmiyorsa; btw, onu benzersiz hale dönüştürmeden önce gerekli diğer işlemleri yapın, muhtemelen bunu yapmanın en hızlı yolu, aynı zamanda çok kısa.

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

o zaman bunu deneyebilirsin

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]

Bu işlevi bu makaleyi okuyarak buldum ...

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

For döngüsünü sevmiyorum. birçok parametreye sahip. while döngüsü gibi. while, hepimizin çok sevdiği tarayıcı dışında tüm tarayıcılarda en hızlı döngü ... chrome.

Neyse ise while kullanan ilk işlevi yazdı.Ve evet bu makalede bulunan işlevden biraz daha hızlı. ama yeterli değil.unique2()

sonraki adım modern js kullanın. Object.keys i döngü için js1.7's Object.keys ile değiştirdi ... biraz daha hızlı ve daha kısa (krom 2x daha hızlı);). Yeterli değil!. unique3().

bu noktada benzersiz fonksiyonumda gerçekten neye ihtiyacım olduğunu düşünüyordum. eski dizi gerekmez, hızlı bir fonksiyon istiyorum. bu yüzden 2 döngüler + ekleme kullandım.unique4()

Etkilendiğimi söylemek işe yaramaz.

krom: Saniyede normal 150.000 işlem saniyede 1.800.000 işleme sıçradı.

yani: 80.000 op / s vs 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

Kanıt http://jsperf.com/wgu veya daha iyi bir kullanım console.time ... mikrotime ... her neyse

unique5() sadece eski diziyi korumak istiyorsanız ne olacağını göstermektir.

Ne Array.prototypeyaptığını bilmiyorsan kullanmayın . sadece bir sürü kopya ve geçmiş yaptım. Object.defineProperty(Array.prototype,...,writable:false,enumerable:false})Yerel bir prototip oluşturmak istiyorsanız kullanın. Örnek: https://stackoverflow.com/a/20463021/2450730

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

NOT: Bu işlemden sonra eski diziniz imha edilir / bildirilir.

yukarıdaki kodu okuyamazsanız, bir javascript kitabı okuyun veya daha kısa kod hakkında bazı açıklamalar. https://stackoverflow.com/a/21353032/2450730

bazıları kullanıyor indexOf ... yapma ... http://jsperf.com/dgfgghfghfghghgfhgfhfghfhgfh

boş diziler için

!array.length||toUnique(array); 

4
100k bir Urls (dizeler) dizisiyle node.js üzerinde test edildi. Sonuç, underscore.js _.uniq'den 2 kat daha yavaştı ... ayrı bir jsperf sizinle aynı fikirde olmasına rağmen ( jsperf.com/uniq-performance/5 ), hayal kırıklığına uğradım :(
xShirase

3
jsperf'de doğru test yapmıyorsunuz ... örneğinizde işlevi her zaman tanımlarsınız ... ancak underscore.js işlevleri zaten tanımlanmıştır .. Bu işlevimi cezalandırır. Ayrıca test 3 ve 4. başka bir şey söylemek gerekir eğer karışık değişkenler (dizeleri ve sayıları) kullanırsanız bir [b]! == a [c] bir [b]! = a [c]
cocco yerine

2
JsPerf'in doğru olup olmadığından emin değilim, ancak Reduce alternatifinin (benim için daha kolay anlaşılır) çözümünüzden biraz% 94 daha hızlı olduğu görülüyor. jsperf.com/reduce-for-distinct Düzenleme: Sizinki kromda daha yavaş, Edge'de aynı ve Firefox'ta daha hızlı.
Victor Ivens

3
Yalnızca küçük diziler / düşük yinelenen yüzdelerle kullanılabilir . Benzersiz olmayan bir nesne bulunduğunda, motor tüm diğer elemanları sola kaydırmaya zorlanır, şu şekilde : 1) üzerlerinde yineleme 2) onları oku 3) bunları yeni bir konuma yazın. Ve o zaman, aynı zamanda yeni o biri birleştirilmiş eleman ile dizi ve sonuç olarak ... Algoritma hızla büyüyüp diziler / daha sık tekrarlar da aşağılamak döner oluşturur. 1000-> 10000-> 50000 ve ~% 40 yinelenen diziler için, ortalama alma süresi 2-> 775-> 19113 ms olacaktır. (boyut 1-> x10-> x5, süre 1-> x10-> x25 olarak değişir).
ankhzet

1
Teşekkürler. Güzel yaklaşım. Peki ya doğru ürün siparişi?
liberborn

28

Buradaki cevapların çoğu yeni başlayanlar için yararlı olmayabilir. Bir dizinin kaldırılması zorsa, prototip zinciri, hatta jQuery hakkında gerçekten bilgi sahibi olacaklar mı?

Modern tarayıcılarda, temiz ve basit bir çözüm, verileri benzersiz değerlerin bir listesi olarak tasarlanmış bir Sette saklamaktır .

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

Dizinin Array.fromsahip olduğu tüm harika yöntemlere (özelliklere) kolayca erişebilmeniz için Set'i bir Diziye dönüştürmek yararlıdır. Aynı şeyi yapmanın başka yolları da vardır . Ancak Array.fromSetler, forEach gibi birçok yararlı özelliğe sahip olduğundan, hiç ihtiyacınız olmayabilir .

Eski Internet Explorer'ı desteklemeniz gerekiyorsa ve bu nedenle Set'i kullanamıyorsanız, basit bir teknik, önceden yeni dizide olup olmadıklarını kontrol ederken öğeleri yeni bir diziye kopyalamaktır.

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

Bunu anında yeniden kullanılabilir hale getirmek için bir fonksiyona koyalım.

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

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

        return result;
    }
}

Yani kopyalardan kurtulmak için şimdi bunu yapardık.

var uniqueCars = deduplicate(cars);

deduplicate(cars)Parçası haline gelir biz adlandırılmış şey sonuç ne zaman fonksiyon tamamlamalar.

Sadece istediğiniz herhangi bir dizinin adını iletin.


Bu arada, tekniğimin esnek olduğunu göstermek için dizelerle dolu bir dizi kullandım. Sayılar için düzgün çalışacaktır.
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;
 }, []);

pushÖğeyi neden kullanmak yerine diziye basitçe eklemediğinizi açıklayabilir misiniz concat? Push'u kullanmayı denedim ve başarısız oldu. Bir açıklama arıyorum.
makenova

1
Nedeni dönüş değeridir. concat değiştirilmiş diziyi döndürür (tam olarak azaltma işlevi içinde döndürülmesi gerekenler), push ise itilen değere erişebileceğiniz bir dizin döndürür. senin sorunun cevabı bu mu?
sergeyz

Görünüşe göre, bu çözüm kromda (ve düğümde) oldukça hızlıdır. jsperf.com/reduce-for-distinct Bu ES6 sürümü: [0,1,2,0,3,2,1,5].reduce((prev, cur) => ~prev.indexOf(cur) ? prev : prev.concat([cur]), []);
Victor Ivens

side note: bu uygulama NaNdostça değil
Alireza

19

Bunu ES6 setlerini kullanarak yapabiliriz:

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

console.log(uniqueArray);

// Çıktı

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

17

Bu prototip getUniquetamamen doğru değil, çünkü i gibi bir Array varsa: ["1",1,2,3,4,1,"foo"]dönecek ["1","2","3","4"]ve "1"dize ve 1bir tamsayı; onlar farklı.

İşte doğru bir çözüm:

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

kullanarak:

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

Yukarıdakiler üretecektir ["1",2,3,4,1,"foo"].


1
Not $foo = 'bar'değişkenleri bildirme PHP yoludur. Javascript ile çalışacak, ancak örtük bir global oluşturacak ve genellikle yapılmamalıdır.
Camilo Martin

@CamiloMartin üzgünüm ama yanılıyorsunuz, $ foo küresel çünkü örnek bir kapanışta değil ve var anahtar sözcüğünü kaçırıyor. Dolar ile ilgisi yok jsfiddle.net/robaldred/L2MRb
Rob

8
@Rob tam olarak söylediğim şey, PHP insanlar $fooaslında iken javascript değişkenleri bildirmenin yolu olduğunu düşünecek var foo.
Camilo Martin

13

Array.prototype (kötü bir uygulama olduğu söylenir) veya jquery / alt çizgi kullanmadan filter, diziyi basitçe yapabilirsiniz .

Son olayı koruyarak:

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

veya ilk olay:

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

Eh, sadece javascript ECMAScript 5+, yani sadece IE9 + anlamına geliyor, ancak yerel HTML / JS'de (Windows Store Uygulaması, Firefox OS, Sencha, Phonegap, Titanium, ...) bir geliştirme için güzel.


2
Onun js 1.6 olması, kullanamayacağınız anlamına gelmez filter. At MDN sayfa onlar Internet Explorer için bir uygulama var, ben eski tarayıcıları demek. Ayrıca: JS 1.6 yalnızca Firefox'un js motoruna atıfta bulunur, ancak bunun ECMAScript 5 olduğunu söylemek için doğru olan şeydir
Camilo Martin

@CamiloMartin I 1.6'yı ECMAScript5 olarak değiştirdi. Teşekkürler.
Cur

13

sihirli

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

O (n) performans ; dizinizin ave içinde olduğunu varsayalım t={}. Burada açıklama (+ Jeppe göstr.)


14
Bu harika görünüyor, sağlam bir açıklama yapmadan bunu çalıştırdığımda bitcoin madeni olacağınızı düştü
Ondřej Želazko

3
ne demek istediğim bazı açıklama ve yorum yapısöküm ile cevabınızı genişletmek gerektiğini. insanların böyle yararlı cevaplar bulmasını beklemeyin. (gerçekten harika görünüyor olsa da muhtemelen çalışır)
Ondřej Želazko

1
@Jeppe senin iyileştirme çalıştırdığınızda o zaman aha etkisi yaşıyorum (Ben döngü indışında diğer inşaat dışında operatör kullanabilirsiniz bilmiyorum önce for: P) - Teşekkür ederim - takdir ediyorum ve diğer iyi cevaplar için +2 verecek .
Kamil Kiełczewski

2
tfiltrelemeden sonra canlı kalan küresel bir değişken yaratmıyor mu … ??
philipp

1
Bu gerçekten zor bir çözüm. Ne yazık ki, genel bir değişken tanımlamak ve / veya genel gizlemek için modül paketleyicinize güvenmek istemiyorsanız, ifadenin dışında t bildirmeniz gerekir. Süslü görünüyor, ama kendinize (ve dev ekibinize) bir iyilik yapın ve sadece iki astar yazın: let t = {}; a.filter (e =>! (t [e] = t
cinsinden

13
[...new Set(duplicates)]

Bu en basit olanıdır ve MDN Web Docs'tan alınmıştır .

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]

Bu kod soruyu çözebilir, ancak bunun sorunun nasıl ve neden çözüldüğüne dair bir açıklama da dahil olmak üzere , yayınınızın kalitesini artırmaya yardımcı olabilir ve muhtemelen daha fazla oyla sonuçlanır. Sadece şimdi soran kişi için değil, gelecekte okuyucular için soruyu cevapladığınızı unutmayın. Lütfen açıklama eklemek için cevabınızı düzenleyin ve hangi sınırlamaların ve varsayımların geçerli olduğunu belirtin.
id.ot

11

Prototip çerçevesini kullanıyorsanız 'for' döngülerine gerek yoktur, http://www.prototypejs.org/api/array/uniq'i şu şekilde kullanabilirsiniz :

var a = Array.uniq();  

Hangi yinelenen ile yinelenen bir dizi üretecektir. Sorunuzla, farklı dizi kayıtlarını saymak için bir yöntem ararken karşılaştım.

uniq ()

kullandım

boyut()

ve basit bir sonucum vardı. ps Özür dilerim bir şey yanlış

edit: tanımlanmamış kayıtlardan kaçmak istiyorsanız, eklemek isteyebilirsiniz

kompakt()

önce, böyle:

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

14
çünkü daha iyi bir cevap buldum, konuların sadece sorduğu kişi için değil tüm insanlar için olduğunu düşünüyorum
Decebal

11

Şimdi kümeleri kullanarak kopyaları kaldırabilir ve diziye geri dönüştürebilirsiniz.

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

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

Başka bir çözüm, sıralama ve filtreyi kullanmaktır

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

Bunun nedeni 0JavaScript'teki yanlış bir değerdir.

this[i] dizinin değeri 0 veya başka herhangi bir falsy değeri ise falsy olur.


Ahhhh, tamam şimdi görüyorum ... ama çalışmasını sağlamak için kolay bir düzeltme olur mu?
Aralık'ta 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
}

Dizi nesneleri / dizileri içeriyorsa bu işe yaramayacağını düşünüyorum ve skaler tür koruyacak emin değilim.
Camilo Martin

Evet, her şey yayılır. Eşitlik karşılaştırması yine de dize olmasına rağmen (olası tüm Javascript eşitliklerinden çok mantıksız görünmese de) , orijinal değer osadece a yerine saklanarak düzeltilebilir 1.
geçici

Array.prototype yalnızca numaralandırılamayan yöntemlerle genişletilebilir .... Object.defineProperty (Array.prototype, "getUnique", {}) ... ancak yardımcı bir nesne kullanma fikri çok güzel
bortunac

10

Ben bir diziden yinelenen kimlik özellikleri ile nesneleri kaldırmak için gerekli biraz farklı bir sorun vardı. bu işe yaradı.

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

En basit cevap:

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

Basit ama bir dizi nesne ile çalışmaz.
Thanwa Ch.

7

Gabriel Silveira'nın işlevi neden bu şekilde yazdığından emin değilim ama benim için de ve minimizasyon olmadan çalışan daha basit bir form:

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

veya CoffeeScript'te:

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

7

Ekstra bağımlılıklar konusunda sorun yaşıyorsanız veya kod tabanınızdaki kitaplıklardan birine zaten sahipseniz, LoDash'i (veya Alt Çizgi'yi) kullanarak bir diziden yinelenenleri kaldırabilirsiniz.

kullanım

Kod tabanınızda zaten yoksa npm kullanarak yükleyin:

npm install lodash

Sonra aşağıdaki gibi kullanın:

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

Dışarı:

[ 1, 2, 3 ]

7

Bu çok cevaplandı, ama benim özel ihtiyacımı karşılamadı.

Birçok cevap şöyle:

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

Ancak bu karmaşık nesnelerin dizileri için geçerli değildir.

Diyelim ki böyle bir dizimiz var:

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

Biz benzersiz adlarla nesneler istiyorsak, kullanmalısınız array.prototype.findIndexyerine array.prototype.indexOf:

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

Harika bir çözüm, yeni bir dizinin bir işlevden geri döneceğine dikkat edin. (kendini değiştirmez)
Thanwa Ch.

6

Gönderen Shamasis Bhattacharya blogu (O (2n) zaman karmaşıklığı):

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

Gönderen Paul İrlanda bireyin blog JQuery üzerinde iyileştirme: .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

Basit yöntemle benzersiz Dizi değerleri bulma

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

Görünüşe göre Rafael'in cevabını kaybettik , birkaç yıldır kabul edilen cevap olarak kaldı. Karışık tip bir diziniz yoksa bu (en azından 2017'de) en iyi performans gösteren çözümdü :

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

Eğer varsa yapmak karışık tip dizi var, karma anahtarını serileştirebilirsiniz:

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

Sorunu başka bir şekilde gidermek için, dizinizi yüklerken, Set nesnesinin yaptığı gibi yinelenen herhangi bir şeyin olmaması yararlı olabilir, ancak henüz tüm tarayıcılarda kullanılamaz. Bellek tasarrufu sağlar ve içeriğine birçok kez bakmanız gerektiğinde daha verimlidir.

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

Örneklem:

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

Sana verir set = [1,3,4,2]

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.