Отримати всі унікальні значення в масиві JavaScript (видалити дублікати)


1483

У мене є масив чисел, який мені потрібно переконатися, що вони унікальні. Нижче я знайшов фрагмент коду в Інтернеті, і він чудово працює, поки масив не має нуля. Я знайшов цей інший скрипт тут на Stack Overflow, який виглядає майже так, як він, але він не виходить з ладу.

Тож заради того, щоб допомогти мені навчитися, чи може хтось допомогти мені визначити, де сценарій прототипу йде не так?

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

Більше відповідей з повторного запитання:

Подібне запитання:


3
@hippietrail Це старше питання про пошук і повернення лише дублікатів (мене також збентежили!). Моє запитання більше про те, чому ця функція не працює, коли масив має нуль.
Мотті

Напевно, ви також хочете зробити назву питання менш розпливчастою.
hippietrail

Для майбутніх читачів, коли ви починаєте знаходити, що вам доведеться весь час алгоритмічно змінювати вміст вашої структури даних (замовляти їх, видаляти повторювані елементи тощо) або шукати елементи всередині нього під час кожної ітерації, можна припустити, що ви в першу чергу використовуйте неправильну структуру даних і починайте використовувати ту, яка є більш підходящою для задачі, яка знаходиться під рукою (у цьому випадку хеш-набір замість масиву).
Нуреттін

Я скопіював код з іншого місця, довгий час тому ... але це здається досить прямолінійним: o= object, a= array, i= indexі e= umm, щось: P
Mottie

Відповіді:


2656

За допомогою JavaScript 1.6 / ECMAScript 5 ви можете використовувати власний filterметод масиву таким чином, щоб отримати масив з унікальними значеннями:

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

Нативний метод filterпройде через масив і залишить лише ті записи, які передають дану функцію зворотного виклику onlyUnique.

onlyUniqueперевіряє, чи задане значення є першим. Якщо ні, він повинен бути дублікатом і не буде скопійований.

Це рішення працює без додаткової бібліотеки, наприклад jQuery або prototype.js.

Він також працює для масивів зі змішаними типами значень.

Для старих браузерів (<ie9), які не підтримують нативні методи, filterі indexOfви можете знайти робочі кола в документації MDN для фільтра та indexOf .

Якщо ви хочете зберегти останнє виникнення значення, просто замініть indexOfйого lastIndexOf.

З ES6 це можна скоротити до цього:

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

Дякуємо Каміло Мартіну за натяк у коментарі.

ES6 має власний об'єкт Setдля зберігання унікальних значень. Щоб отримати масив з унікальними значеннями, ви можете зробити це зараз:

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

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

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

Конструктор з Setприймає ітерабельний об'єкт, як-от Array, а оператор розповсюдження ...перетворює набір назад у масив. Дякуємо Лукасу Лізе за натяк у коментарі.


53
На жаль, це рішення буде працювати набагато повільніше. Ви лупцюєте двічі, один раз з фільтром і один раз з індексом
Джек Францен

18
@JackFranzen повільніше, ніж що? Рішення від Рафаеля ? Рішення Rafaels не працює для масивів змішаного типу. Для мого прикладу ['a', 1, 'a', 2, '1']ви отримаєте ['a', 1, 2]. Але це не те, чого я очікував. До речі, набагато повільніше дуже відносно.
TLindig

25
У сучасному JS: .filter((v,i,a)=>a.indexOf(v)==i)(позначення жирової стрілки).
Каміло Мартін


5
Більш детальну відповідь, включаючи безліч можливостей - наприклад, сортування спочатку та розгляд різних типів даних - див. Stackoverflow.com/a/9229821/368896
Dan Nissenbaum

873

Оновлена ​​відповідь для ES6 / ES2015 : Використовуючи набір , рішення для єдиного рядка:

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

Який повертається

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

Як пропонує le_m , це також можна скоротити, використовуючи оператор розширення , наприклад

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

10
Зауважте, що внутрішній масив не спрацюєArray.from(new Set([[1,2],[1,2],[1,2,3]]))
Олександр Гончаров

2
Продуктивність цього рішення порівняно з myArray.filter((v, i, a) => a.indexOf(v) === i);?
Симой

42
Зверніть увагу, що якщо ви використовуєте Setі додаєте об'єкти замість примітивних значень, вони будуть містити унікальні посилання на об'єкти. Таким чином, безліч sв let s = new Set([{Foo:"Bar"}, {Foo:"Bar"}]);поверне це: Set { { Foo: 'Bar' }, { Foo: 'Bar' } }що є Setз унікальними посиланнями на об'єкти на об'єкти , які містять одні і ті ж значення. Якщо ви let o = {Foo:"Bar"};let s2 = new Set([o,o]);Set { { Foo: 'Bar' } }
напишете,

2
Обидва ці варіанти мають проблеми з IE10 навіть при наявності шару polyfill.io
Thymine

2
новий тестовий випадок jsperf.com/array-filter-unique-vs-new-set/1 схожий new Setна трофей
shuk

167

Я усвідомлюю, що на це питання вже є понад 30 відповідей. Але я перечитав спочатку всі існуючі відповіді та зробив власне дослідження.

Я розділив усі відповіді на 4 можливі рішення:

  1. Використовуйте нову функцію ES6: [...new Set( [1, 1, 2] )];
  2. Використовуйте об'єкт { }для запобігання дублікатів
  3. Використовуйте допоміжний масив [ ]
  4. Використовуйте filter + indexOf

Ось зразкові коди, знайдені у відповідях:

Використовуйте нову функцію ES6: [...new Set( [1, 1, 2] )];

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

Використовуйте об'єкт { }для запобігання дублікатів

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

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

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

Використовуйте допоміжний масив [ ]

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

Використовуйте 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;
}

І я задумався, хто з них швидший. Я зробив зразок Google Sheet для тестування функцій. Примітка. ECMA 6 недоступний у Google Таблицях, тому я не можу перевірити його.

Ось результат тестів: введіть тут опис зображення

Я очікував, що код, що використовує об'єкт { }, виграє, оскільки він використовує хеш. Тож я радий, що тести показали найкращі результати для цього алгоритму в Chrome та IE. Дякуємо @rab за код .


Опція "filter + indexOf" надзвичайно повільна для масивів понад 100 000 елементів. Мені довелося використовувати підхід "об'єктна карта", однак це порушує оригінальне сортування.
ліберборн

Чи більша кількість повільніше, не швидше?
fletchsod

3
@ fletchsod, числа - це час у мс для запуску коду.
Макс Махров

1
Цифри Вонг! Сценарій додатків Google працює на їх сервері, а не на веб-переглядачі клієнта - і тому продуктивність у Chrome / IE (або практично будь-якому браузері) буде однаковою!
Джей Даданія

1
Хоча можна запустити JS на стороні клієнта з Google Script (або через google.script.host або google.script.run - не впевнений, який), у відповіді чітко сказано, що "ECMA 6 не доступний у Google Sheets" - тому ми можемо сміливо припускати, що плакат використовував для цього сервер Google Script - і, таким чином, відповідь показує продуктивність сервера Google, а не браузера !!
Джей Даданія

134

Ви також можете використовувати underscore.js .

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

який повернеться:

[1, 2, 3, 4]

22
Будь ласка, зробіть це люди. Не кладіть щось на прототип Array. Будь ласка.
Джейкоб Далтон

5
@JacobDalton - це не розширює прототип масиву. Він розміщений з іменами в об'єкті _.
надсвітні

24
@superluminary Я знаю, тому я сказав, будь ласка, зробіть це. Прийняте рішення пропонує змінити прототип Array. НЕ робіть цього.
Джейкоб Далтон

20
@JacobDalton Будь ласка, не робіть цього. Не потрібно додавати додаткову бібліотеку лише для невеликої роботи, яку можна виконатиarray = [...new Set(array)]

3
Це перемогло мету використання функцій ES та додавання більшої кількості бібліотек лише більше роздуває веб-сайт.
fletchsod

68

Один вкладиш, чистий JavaScript

З синтаксисом 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")

введіть тут опис зображення

З синтаксисом ES5

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

Сумісність браузера : IE9 +


13
@Spets Це квадратична вартість, тому це не найкраща відповідь
Oriol

@ Як, наприклад, всі унікальні / відмінності ... так, будьте розумні, використовуйте radix для сортування спочатку, будьте повільнішими за швидкий пошук 0 (n2) у більшості випадків.
докер

Дякую, хлопці! Я не усвідомлював цього, коли я вперше подивився на код, але тепер його трохи більш очевидно.
Спец

51

З тих пір я знайшов хороший метод, який використовує jQuery

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

Примітка: Цей код був знятий з пункту пробивання качки Пола Ірландського - я забув дати кредит: P


8
Коротке рішення, але виклик inArray набагато менш ефективний, ніж виклик hasOwnProperty.
Містер Сміт

Це також O (N ^ 2), правда? Тоді як підхід до словника або hasOwnProperty, ймовірно, буде O (N * logN).
швидкісний літак

Це працювало для мене, всі інші рішення не підтримувалися Internet Explorer.
kerl

51

Найкоротше рішення з ES6: [...new Set( [1, 1, 2] )];

Або якщо ви хочете змінити прототип масиву (як в оригінальному запитанні):

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

Наразі EcmaScript 6 лише частково реалізований у сучасних браузерах (серпень 2015 р.), Але Babel стала дуже популярною для перенесення ES6 (і навіть ES7) назад в ES5. Таким чином ви сьогодні можете написати код ES6!

Якщо вам цікаво, що ...означає, це називається оператором розповсюдження . Від MDN : «Оператор розповсюдження дозволяє розширити вираз у місцях, де очікується багато аргументів (для викликів функцій) або декількох елементів (для літералів масиву)». Оскільки набір є ітерабельним (і може мати лише унікальні значення), оператор розповсюдження розширить набір для заповнення масиву.

Ресурси для вивчення ES6:


3
ви можете зробити це ще коротше, a = [...Set(a)]але, у будь-якому випадку, це лише Firefox.
c69

@ c69, правильно, не стане коротшим за це. Також оцінять користувачів SpiderMonkey.
Торстен Бекер

Працює з Бабелем. Вам просто потрібно включити Array.fim shim також:require ( "core-js/fn/array/from" );
Владислав Раструсний,

чи має бути Array.prototype.getUnique = function () {return [... новий встановити ([це])]; }; або, Array.prototype.getUnique = function () {return [... новий встановити (це)]; }; Я застосував його на наступному- $ ('body'). Html (). ToLowerCase (). Match (/ ([a-zA-Z0-9 ._ + -] + @ [a-zA-Z0-9). _-] + \. [a-zA-Z0-9 ._-] +) / gi) .getUnique (); Перший повернув мені одне унікальне значення лише там, де другий переробив усі видалення дублікатів.
ashique

[...Set(['a', 1, 'a', 2, '1'])]викине TypeError, тому все-таки розумно зберегти new:[...new Set(['a', 1, 'a', 2, '1'])]
Адам Кац

36

Найпростіше рішення:

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

Або:

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


4
Зауважте, що це не підтримується в IE10 та нижче. Зауважте, що IE11 + та Safari пропонують обмежену підтримку (навіть не впевнений, що приклад працює вище) developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
oriadam

32

Найпростіший і найшвидший (у Chrome) спосіб зробити це:

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

Просто проходить кожен елемент у масиві, перевіряє, чи цей елемент уже є у списку, а якщо його немає, натисніть на масив, який повертається.

За словами jsPerf, ця функція є найшвидшою з тих, кого я могла знайти де завгодно - хоч і не соромтесь додати свою власну.

Не прототип версії:

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

Сортування

Якщо також потрібно сортувати масив, найшвидшим є наступне:

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

або непрототип:

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

Це також швидше, ніж описаний вище метод у більшості нехромованих браузерів.


В Linux Chrome 55.0.2883 віддає перевагу arr.unique (), а arrclone2.sortFilter () є найбільш повільним (на 78% повільніше). Однак, Firefox 51.0.0 (з великою кількістю доповнень) має швидкісні вершини (але все-таки повільніше на Ops / сек, ніж будь-який інший результат Chrome), а jQuery $ .grep (arr, jqFilter) мотива є найповільнішим (на 46% повільніше). Ваш arr.uniq () був на 30% повільніше. Я проходив кожен тест двічі і отримував стійкі результати. Рафаель arr.getUnique () отримав друге місце в обох браузерах.
Адам Кац

На даний момент jsPerf баггі , тому моє редагування цього тесту не здійснило все, але це призвело до додавання двох тестів: toUnique Cocco () перемагає ES6 list.filter () обох браузерів, побиваючи сорт фільтра фільму () для №1 у FF (сортування фільтру було на 16% повільніше) та побороне сортування тестування (яке було повільніше на 2%) для №3 у Chrome.
Адам Кац

Ах, я не зрозумів, що ці тести були тривіально малі і насправді не мають значення. Коментар до прийнятої відповіді описує цю проблему і пропонує виправлення в перегляді тесту, в якому код Рафаеля легко найшвидший, а код arr.unique Joetje50 - на 98% повільніше. Я також зробив ще одну редакцію, як було зазначено в цьому коментарі .
Адам Кац

1
Ну, насправді алгоритм, який ви реалізуєте у uniqueфункції, має складність O (n ^ 2), тоді як алгоритм getUnique- O (n). Перший може бути швидшим на малих наборах даних, але як можна сперечатися з математикою :) Ви можете переконатися, що останній швидше, якщо запустити його на масив, скажімо, 1e5 унікальних елементів
Михайло Дудін

31

ТІЛЬКІСТЬ! цей код, ймовірно, у 10 разів швидший, ніж усі коди тут * працює у всіх браузерах, а також має найменший вплив пам’яті .... і багато іншого

якщо вам не потрібно повторно використовувати старий масив; btw зробіть необхідні інші операції, перш ніж перетворити його на унікальний тут, мабуть, найшвидший спосіб зробити це, також дуже короткий.

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

то ви можете спробувати це

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]

Я придумав цю функцію, читаючи цю статтю ...

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

Мені не подобається цикл for. він має багато параметрів. Я люблю цикл while--. в той час як це найшвидший цикл у всіх браузерах, крім того, який ми всі так любимо ... хром.

у будь-якому випадку я написав першу функцію, яка використовує while.Aep так це трохи швидше, ніж функція, знайдена в Article.але недостатньо.unique2()

Наступний крок - використання сучасних js. Object.keys я замінив інший для циклу на Object.keys js1.7 ... трохи швидше і коротше (у хромі на 2 рази швидше);). Недостатньо!. unique3().

в цей момент я думав про те, що мені справді потрібно в моїй унікальній функції. мені не потрібен старий масив, я хочу швидку функцію. тому я використав 2, а петлі + зрощення.unique4()

Марно сказати, що я був вражений.

хром: звичайні 150 000 операцій в секунду підскочили до 1800 000 операцій в секунду.

тобто: 80 000 оп / с проти 3 500 000 оп / с

ios: 18 000 op / s проти 170 000 op / s

сафарі: 80 000 оп / с проти 6 000 000 оп / с

Доказ http://jsperf.com/wgu або краще використовувати console.time ... microtime ... все, що завгодно

unique5() це просто показати вам, що відбувається, якщо ви хочете зберегти старий масив.

Не використовуйте, Array.prototypeякщо ви не знаєте, чим займаєтесь. Я щойно зробив багато копій та минулий. Використовуйте, Object.defineProperty(Array.prototype,...,writable:false,enumerable:false})якщо ви хочете створити власний prototype.example: https://stackoverflow.com/a/20463021/2450730

Демонстрація http://jsfiddle.net/46S7g/

ПРИМІТКА: ваш старий масив після цієї операції знищується / стаєуніке.

якщо ви не можете прочитати код вище, запитайте, прочитайте книгу javascript або ось кілька пояснень щодо коротшого коду. https://stackoverflow.com/a/21353032/2450730

деякі використовують indexOf ... не ... http://jsperf.com/dgfgghfghfghghgfhgfhfghfhgfh

для порожніх масивів

!array.length||toUnique(array); 

4
перевірено на node.js, з масивом 100k Urls (рядки). Результат був у 2 рази повільніший, ніж underscore.js _.uniq ... хоча окремий jsperf погоджується з вами ( jsperf.com/uniq-performance/5 ), я розчарований :(
xShirase

3
ви не тестуєте правильно в jsperf ... у вашому прикладі ви визначаєте функцію кожного разу ... але функції underscore.js вже визначені .. це карає мою функцію. також тестуйте 3 і 4. Ще одна річ, яку я повинен сказати, це те, що якщо ви використовуєте змішані змінні (рядки та числа), ви повинні замінити [b]! == a [c] на [b]! = a [c]
cocco

2
Я не впевнений, чи правильно вийшов jsPerf, але здається, що альтернатива зменшення (що мені легше зрозуміти) дещо на 94% швидше, ніж ваше рішення. jsperf.com/reduce-for-distinct Редагувати: ваш хром повільніше, той самий на Edge та швидший у Firefox.
Віктор Івенс

3
Використовується лише з невеликими відсотками масивів / низькими копіями . Кожного разу, коли буде знайдено унікальний об'єкт, двигун буде змушений зміщувати всі інші елементи вліво, шляхом: 1) повторення над ними 2) зчитування їх 3) записування їх на нове місце. А потім, він також створює новий масив з цим з’єднаним елементом і повертає його в результаті ... Алгоритм швидко деградує з більшими масивами / більш частими дублікатами. Для масивів розміром 1000-> 10000-> 50000 та ~ 40% дублікатів середній зайнятий час становитиме 2-> 775-> 19113 мс. (розмір змінюється як 1-> x10-> x5, час як 1-> x10-> x25) у Chrome.
ankhzet

1
Дякую. Гарний підхід. Але як щодо правильного порядку замовлення товарів?
ліберборн

28

Багато відповідей тут, можливо, не корисні новачкам. Якщо видалити копію масиву важко, вони дійсно будуть знати про прототип ланцюга чи навіть jQuery?

У сучасних веб-переглядачах чистим і простим рішенням є зберігання даних у наборі , який призначений для переліку унікальних значень.

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

Array.fromКорисно перетворити Set назад в масив , так що у вас є вільний доступ до всіх з дивних методів (функцій) , що масиви мають. Є й інші способи зробити те саме. Але вам це взагалі не знадобиться Array.from, оскільки набори мають безліч корисних функцій, таких як forEach .

Якщо вам потрібно підтримувати старий Internet Explorer, і тому ви не можете використовувати Set, то простою технікою є копіювання елементів у новий масив, попередньо перевіряючи, чи вони вже є в новому масиві.

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

Щоб зробити це миттєво багаторазовим використання, давайте поставимо його у функцію.

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

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

        return result;
    }
}

Отже, щоб позбутися дублікатів, ми б зараз це зробили.

var uniqueCars = deduplicate(cars);

deduplicate(cars)Частина стає річчю ми назвали результат , коли функція завершується.

Просто передайте ім'я будь-якого вподобаного масиву.


До речі, я використав масив, повний рядків, щоб показати, що моя техніка гнучка. Він буде працювати належним чином для чисел.
Сет Холладай

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елемент у масив, а не використовували concat? Я спробував використовувати push, і це не вдалося. Я шукаю пояснення.
makenova

1
Причина полягає у зворотному значенні. concat повертає модифікований масив (саме те, що потрібно повернути всередині функції зменшення), тоді як push повертає індекс, за яким можна отримати доступ до висунутого значення. Це відповідає на ваше запитання?
sergeyz

Мабуть, це рішення досить швидко хромується (і вузол). jsperf.com/reduce-for-distinct Ось версія ES6: [0,1,2,0,3,2,1,5].reduce((prev, cur) => ~prev.indexOf(cur) ? prev : prev.concat([cur]), []);
Віктор Івенс

Примітка сторони: ця реалізація не NaNсодружественная
Реза

19

Це можна зробити за допомогою наборів ES6:

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

console.log(uniqueArray);

// Вихід буде

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

17

Цей прототип getUniqueне зовсім коректний, тому що якщо у мене є масив типу: ["1",1,2,3,4,1,"foo"]він повернеться ["1","2","3","4"]і "1"є рядковим і 1є цілим числом; вони різні.

Ось правильне рішення:

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

використовуючи:

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

Наведене вище призведе ["1",2,3,4,1,"foo"].


1
Зауважте, що $foo = 'bar'це PHP спосіб декларування змінних. Він буде працювати в javascript, але створить неявну глобальну ситуацію, і взагалі цього робити не слід.
Каміло Мартін

@CamiloMartin вибачте, але ви помиляєтеся, $ foo є глобальним, тому що приклад не закривається, і у нього відсутнє ключове слово var. Нічого спільного з доларом jsfiddle.net/robaldred/L2MRb
Роб

8
@Rob це саме те, що я говорю, люди PHP вважають, що $fooце спосіб декларування змінних у javascript, хоча насправді var fooє.
Каміло Мартін

13

Не розширюючи Array.prototype (це кажуть, що це погана практика) або використовуючи jquery / підкреслення, ви можете просто filterмасив.

Зберігаючи останнє явище:

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

або перше виникнення:

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

Ну, це лише javascript ECMAScript 5+, що означає лише IE9 +, але це приємно для розвитку в рідному HTML / JS (додаток для Windows Store, Firefox OS, Sencha, Phonegap, Titanium, ...).


2
Те, що це js 1.6, не означає, що ви не можете користуватися filter. На сторінці MDN вони мають реалізацію для Internet Explorer, я маю на увазі старіші браузери. Також: JS 1.6 посилається лише на js-движок Firefox, але правильно сказати, що це ECMAScript 5.
Camilo Martin

@CamiloMartin Я змінив 1.6 на ECMAScript5. Дякую.
Cœur

13

Магія

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

O (n) продуктивність ; ми припускаємо, що ваш масив знаходиться в aі t={}. Пояснення тут (+ імп . Jeppe )


14
це виглядає настільки круто, що без твердого пояснення я впав, ти збираєшся видобувати біткойни, коли я запускаю це
Ondřej Želazko

3
що я мав на увазі, що ви повинні розширити свою відповідь деяким поясненням і прокоментувавши її деконструкцію. не сподівайтеся, що люди знайдуть такі корисні відповіді. (хоча це справді виглядає круто, напевно, працює)
Ondřej Želazko

1
@Jeppe, коли я запускаю ваше вдосконалення, я відчуваю аха-ефект (до цього я не знаю, що я можу використовувати inоператор поза іншою конструкцією, ніж forцикл: P) - Дякую - я ціную це і дам +2 вашим іншим хорошим відповідям .
Kamil Kiełczewski

2
Хіба це не створює глобальну змінну, tяка залишається живою після фільтрації… ??
філіпп

1
Це дійсно хитро рішення. На жаль, вам потрібно оголосити t поза виразу, якщо ви не хочете визначити глобальну змінну та / або покластися на свій модуль, щоб приховати глобальний. Це виглядає фантазією, але зробіть собі (і свою команду розробників) послугу і просто напишіть два лайнери: нехай t = {}; a.filter (e =>! (t [e] = e in t))
ford04

13
[...new Set(duplicates)]

Це найпростіший і на який посилаються 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]

Хоча цей код може вирішити питання, включаючи пояснення, як і чому це вирішує проблему, справді допоможе покращити якість вашої публікації та, ймовірно, призведе до збільшення кількості голосів. Пам'ятайте, що ви відповідаєте на запитання читачів у майбутньому, а не лише про людину, яка зараз задає питання. Будь ласка, відредагуйте свою відповідь, щоб додати пояснення та вказати, які обмеження та припущення застосовуються.
id.ot

11

Якщо ви використовуєте прототипний фреймворк, немає необхідності робити цикли 'for', ви можете використовувати http://www.prototypejs.org/api/array/uniq так:

var a = Array.uniq();  

Який створить дублікат масиву без дублікатів. Я натрапив на ваше запитання, шукаючи спосіб підрахувати різні записи масиву, щоб потім

uniq ()

я використав

розмір ()

і був мій простий результат. ps Вибачте, якщо я щось неправильно ввів

редагувати: якщо ви хочете уникати невизначених записів, ви можете додати

компактний ()

раніше, як це:

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

14
тому що я знайшов найкращий відповідь, я думаю про темах , є для всіх людей , а не тільки для того, хто запитав
Decebal

11

Тепер за допомогою наборів ви можете видалити дублікати та перетворити їх назад у масив.

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

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

Іншим рішенням є використання сортування та фільтрування

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

Це тому 0, що це хибне значення в JavaScript.

this[i] буде помилковим, якщо значення масиву дорівнює 0 або будь-яке інше значення фальсифікації.


А-а-а, добре, я бачу зараз ... але чи буде легке виправлення, щоб воно працювало?
Мотті

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
}

Я думаю, що це не спрацює, якщо масив містить об’єкти / масиви, і я не впевнений, чи збереже він тип скалярів.
Каміло Мартін

Так, все ускладнюється. Це можна виправити, зберігаючи вихідне значення, oа не просто a 1, хоча порівняння рівності все одно буде строго (хоча, з усіх можливих рівностей Javascript, це не здається занадто необґрунтованим).
ефемія

Прототип Array.prototype можна поширити лише за допомогою незліченних методів .... Object.defineProperty (Array.prototype, "getUnique", {}) ... але ідея використання хелперного об'єкта дуже приємна
Bortunac

10

У мене була дещо інша проблема, коли мені потрібно було видалити об’єкти з подвійними властивостями id з масиву. це спрацювало.

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

Найпростіша відповідь:

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

Простий, але він не працює з масивом об'єктів.
Тхан Ч.

7

Я не впевнений, чому Габріель Сільвейра написав цю функцію саме так, але простіша форма, яка працює для мене так само добре і без мінімізації:

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

або в CoffeeScript:

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

7

Якщо у вас все в порядку із додатковими залежностями або у вас вже є одна з бібліотек у вашій кодовій базі, ви можете видалити дублікати з масиву на місці, використовуючи LoDash (або підкреслення).

Використання

Якщо у вас ще немає кодової бази, встановіть її за допомогою npm:

npm install lodash

Потім використовуйте його наступним чином:

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

Вийшов:

[ 1, 2, 3 ]

7

На це відповіли багато, але це не відповіло на мою особливу потребу.

Багато відповідей виглядають так:

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

Але це не працює для масивів складних об'єктів.

Скажіть, у нас такий масив:

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

Якщо ми хочемо, щоб об'єкти мали унікальні імена, ми повинні використовувати array.prototype.findIndexзамість array.prototype.indexOf:

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

Чудове рішення, будьте обережні, що новий масив повернеться з функції. (він не змінює себе)
Thanwa Ch.

6

Від Shamasis Бхаттачарія блогу «s (O (2n) тимчасової складності):

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

З блогу Пола Ірландського : поліпшення 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

Пошук унікальних значень масиву простим методом

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

Здається, ми втратили відповідь Рафаеля , яка стояла як прийнята відповідь протягом декількох років. Це було (принаймні у 2017 році), найкращим рішенням, якщо у вас немає масиву змішаного типу :

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

Якщо ви робите є масив змішаного типу, ви можете серіалізовать хеш - ключ:

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

Щоб вирішити проблему навпаки, може бути корисним відсутність дубліката під час завантаження масиву, як це зробить Set object, але він доступний не у всіх браузерах. Це економить пам'ять і є більш ефективним, якщо вам потрібно переглянути його вміст багато разів.

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

Зразок:

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

Дає тобі set = [1,3,4,2]

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.