Получить все уникальные значения в массиве JavaScript (удалить дубликаты)

1489

У меня есть массив чисел, которые я должен убедиться, что они уникальны. Я нашел фрагмент кода ниже в интернете, и он прекрасно работает, пока в массиве нет нуля. Я нашел этот другой скрипт здесь, в Переполнении стека, который выглядит почти так же, как и он, но он не дает сбоя.

Таким образом, ради того, чтобы помочь мне учиться, может ли кто-нибудь помочь мне определить, в чем проблема с прототипом скрипта?

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

Больше ответов от дублирующего вопроса:

Подобный вопрос:

Mottie
источник
3
@hippietrail Этот старый вопрос касается поиска и возврата только дубликатов (я тоже был смущен!). Мой вопрос больше о том, почему эта функция не работает, когда массив имеет ноль.
Mottie
Вы, вероятно, хотите, чтобы название вопроса было менее неопределенным.
hippietrail
Для будущих читателей, когда вы обнаружите, что вам необходимо все время алгоритмически изменять содержимое вашей структуры данных (упорядочивать их, удалять повторяющиеся элементы и т. Д.) Или искать элементы внутри нее на каждой итерации, можно с уверенностью предположить, что вы Вы используете неправильную структуру данных в первую очередь и начинаете использовать ту, которая больше подходит для поставленной задачи (в данном случае вместо массива используется хэш-набор).
Нуреттин
Я скопировал код откуда-то еще, очень давно ... но он кажется довольно простым: o= object, a= array, i= indexи e= ммм, что-то: P
Mottie
Возможный дубликат Как получить уникальные значения в массиве
Адел Имран

Ответы:

2662

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

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

Спасибо Camilo Martin за подсказку в комментарии.

ES6 имеет собственный объект Setдля хранения уникальных значений. Чтобы получить массив с уникальными значениями, вы можете сделать это:

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

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

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

Конструктор объекта Setпринимает итеративный объект, такой как Array, а оператор распространения ...преобразует набор обратно в Array. Спасибо Лукасу Лизе за подсказку в комментарии.

TLindig
источник
55
Это решение, к сожалению, будет работать намного медленнее. Ты зациклишься дважды, один раз с фильтром и один раз с индексом
Джек Франзен
19
@JackFranzen Медленнее чем? Решение от Рафаэля ? Рафаэльс решение не работает для массивов смешанного типа. Для моего примера ['a', 1, 'a', 2, '1']вы получите ['a', 1, 2]. Но это не то, что я ожидал. Кстати, гораздо медленнее, очень относительный.
TLindig
25
В современном JS: .filter((v,i,a)=>a.indexOf(v)==i)(жирная стрелка обозначения).
Камило Мартин
165
let unique_values = [...new Set(random_array)]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Лукас Лиезис
5
Для более подробного ответа, в том числе много возможностей - таких как сортировка первый, и дело с различными типами данных - см stackoverflow.com/a/9229821/368896
Dan Nissenbaum
878

Обновленный ответ для ES6 / ES2015 : Используя Set , однострочное решение:

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)]
В
источник
11
Обратите внимание, что внутренний массив не будет работатьArray.from(new Set([[1,2],[1,2],[1,2,3]]))
Александр Гончаров
3
Производительность этого решения по сравнению с myArray.filter((v, i, a) => a.indexOf(v) === i);?
Simoyw
43
Обратите внимание, что если вы будете использовать Setобъекты и добавлять вместо примитивных значений, он будет содержать уникальные ссылки на объекты. Таким образом, set sin let s = new Set([{Foo:"Bar"}, {Foo:"Bar"}]);будет возвращать следующее: Set { { Foo: 'Bar' }, { Foo: 'Bar' } }это Setуникальные ссылки на объекты, содержащие одинаковые значения. Если вы напишите, let o = {Foo:"Bar"};а затем создадите набор с двумя ссылками, например, так: let s2 = new Set([o,o]);s2 будетSet { { Foo: 'Bar' } }
mortb
2
Оба этих варианта имеют проблемы на IE10 , даже с polyfill.io слой присутствует
тимин
2
новый тестовый случай jsperf.com/array-filter-unique-vs-new-set/1 выглядит как new Setтрофей
шук
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 Sheets, поэтому я не могу его протестировать.

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

Я ожидал увидеть, что код, использующий объект { }, победит, потому что он использует хэш. Поэтому я рад, что тесты показали лучшие результаты для этого алгоритма в Chrome и IE. Спасибо @rab за код .

Макс Махров
источник
Опция «filter + indexOf» очень медленная для массивов более 100 000 элементов. Мне пришлось использовать подход «карта объекта», однако это нарушает оригинальную сортировку.
liberborn
Чем выше число, тем медленнее, а не быстрее?
fletchsod
3
@ fletchsod, числа - это время в мс для запуска кода.
Макс Махров
1
Номера вонг! Скрипт Google Apps запускается на их сервере, а не в браузере клиента - поэтому производительность в Chrome / IE (или практически в любом браузере) будет одинаковой!
Джей Дадхания
1
Хотя можно запускать на стороне клиента JS от Google Script (либо через google.script.host или google.script.run - не уверен , какой), ответ четко говорится , что «ECMA 6 не доступно в Google Sheets» - поэтому мы можем с уверенностью предположить, что автор использовал для этого серверный сценарий Google - и, таким образом, ответ показывает производительность сервера 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]
kornfridge
источник
22
Пожалуйста, сделайте это, ребята. Не надевайте что-либо на прототип Array. Пожалуйста.
Джейкоб Далтон
5
@JacobDalton - это не расширение прототипа массива. Это пространство имен в объекте _.
суперсветовой
25
@superluminary Я знаю, поэтому я сказал, пожалуйста, сделайте это. В принятом решении предлагается модифицировать прототип Array. НЕ делай этого.
Джейкоб Далтон
21
@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 +

Vamsi
источник
14
@ Спец Это имеет квадратичную стоимость, так что это не самый лучший ответ
Oriol
@ Спеты, как и все уникальные / отличительные особенности ... да, будьте умны, сначала используйте radix для сортировки, в большинстве случаев медленнее 0 (n2) быстрого поиска
Докер
Спасибо, парни! Не осознавал, когда я впервые посмотрел на код, но теперь он стал немного более очевидным.
Спец
51

С тех пор я нашел хороший метод, который использует jQuery

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

Примечание. Этот код был взят из сообщения Уши Пола Айриша: я забыл отдать должное: P

Mottie
источник
8
Краткое решение, но вызов inArray гораздо менее эффективен, чем вызов hasOwnProperty.
Мистер Смит
Это тоже O (N ^ 2), верно? Принимая во внимание, что подход словаря или hasOwnProperty, вероятно, будет O (N * logN).
скоростной самолет
Это сработало для меня, все другие решения не были поддержаны Internet Explorer.
Керл
51

Самое короткое решение с ES6: [...new Set( [1, 1, 2] )];

Или, если вы хотите изменить прототип Array (как в оригинальном вопросе):

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.from также:require ( "core-js/fn/array/from" );
Владислав Раструсный
должно быть Array.prototype.getUnique = function () {return [... new Set ([this])]; }; или Array.prototype.getUnique = function () {return [... new Set (this)]; }; Я применил его к следующему: $ ('body'). Html (). ToLowerCase (). Match (/ ([a-zA-Z0-9 ._ + -] + @ [a-zA-Z0-9. . _-] + \ [A-Za-Z0-9 ._-] +) / г) .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;
}

Это также быстрее, чем описанный выше метод в большинстве браузеров без Chrome.

Joeytje50
источник
В Linux Chrome 55.0.2883 предпочитает arr.unique (), а свилимам arrclone2.sortFilter () медленнее (на 78% медленнее). Тем не менее, Firefox 51.0.0 (с большим количеством аддонов) имеет самые быстрые свилиамы (но все же медленнее на число операций в секунду, чем любой другой результат Chrome), а jQuery $ .grep (arr, jqFilter) mottie - медленнее (медленнее на 46%). Ваш arr.uniq () был на 30% медленнее. Я проводил каждый тест дважды и получал последовательные результаты. Рафаэль arr.getUnique () занял второе место в обоих браузерах.
Адам Кац
В данный момент jsPerf содержит ошибки , поэтому мое редактирование этого теста не зафиксировало все, но оно привело к добавлению двух тестов: toUnique () Cocco превосходит ES6 list.filter () Vamsi в обоих браузерах, опережая sortFilter () swilliams за # 1 на FF (sortFilter был на 16% медленнее) и опередил отсортированное тестирование (которое было медленнее на 2%) на # 3 на Chrome.
Адам Кац
Ах, я не понял, что эти тесты были незначительными и не имели большого значения. Комментарий к принятому ответу описывает эту проблему и предлагает исправление в пересмотре теста, в котором код Рафаэля является самым быстрым, а код arr.unique Joetje50 - на 98% медленнее. Я также сделал еще один пересмотр, как отмечено в этом комментарии .
Адам Кац
1
Ну, на самом деле алгоритм, который вы реализовали в uniqueфункции, имеет сложность O (n ^ 2), в то время как алгоритм getUniqueO (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 - самый быстрый цикл во всех браузерах, кроме того, который нам всем так нравится ... chrome.

Во всяком случае, я написал первую функцию, которая использует while. И да, это немного быстрее, чем функция, найденная в статье. Но недостаточно.unique2()

Следующий шаг использовать современные JS. Object.keys я заменил другой цикл for на js1.7 Object.keys ... немного быстрее и короче (в chrome в 2 раза быстрее);). Недостаточно!. unique3(),

в этот момент я думал о том, что мне действительно нужно в МОЕЙ уникальной функции. мне не нужен старый массив, я хочу быструю функцию. так что я использовал 2 цикла + петли.unique4()

Бесполезно говорить, что я был впечатлен.

хром: обычные 150 000 операций в секунду подскочили до 1 800 000 операций в секунду.

то есть: 80 000 операций / с против 3 500 000 операций / с

IOS: 18 000 операций / с против 170 000 операций / с

сафари: 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 с массивом URL-адресов в 100 тыс. строк Результат оказался в 2 раза медленнее, чем underscore.js _.uniq ... хотя с вами согласен отдельный jsperf ( jsperf.com/uniq-performance/5 ), я разочарован :(
xShirase
3
вы не тестируете правильно в jsperf ... в вашем примере вы определяете функцию каждый раз ... но функции underscore.js уже определены ... это наказывает мою функцию. также проверьте 3 и 4. Еще одна вещь, которую я должен сказать: если вы используете смешанные переменные (строки и числа), вы должны заменить a [b]! == a [c] на a [b]! = a [c]
cocco
2
Я не уверен, правильно ли выполнил jsPerf, но кажется, что альтернатива Reduce (что для меня легче понять) на несколько процентов быстрее, чем ваше решение. jsperf.com/reduce-for-distinct Редактировать: у вас медленнее в Chrome, то же самое в Edge и быстрее в Firefox.
Виктор Ивенс
3
Используется только с небольшими массивами / низким процентом дубликатов . Каждый раз, когда обнаруживается неуникальный объект, движок будет вынужден сдвигать все остальные элементы влево: 1) перебирать их 2) читать их 3) записывать их в новое место. И затем, он также создает новый массив с этим одним соединенным элементом и возвращает его как результат ... Алгоритм быстро ухудшается с большими массивами / более частыми дубликатами. Для массивов размером 1000-> 10000-> 50000 и ~ 40% дубликатов среднее время выполнения будет равно 2-> 775-> 19113 мс. (размер изменяется как 1-> x10-> x5, время как 1-> x10-> x25) в Chrome.
Анхзет
1
Спасибо. Хороший подход. Но как насчет правильного заказа товаров?
liberborn
28

Многие из ответов здесь могут быть не полезны для начинающих. Если дедупликация массива затруднительна, они действительно будут знать о цепочке прототипов или даже jQuery?

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

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

Это Array.fromполезно для преобразования Set обратно в массив, чтобы у вас был легкий доступ ко всем удивительным методам (функциям), которые есть у массивов. Есть и другие способы сделать то же самое. Но вам может и не понадобиться Array.from, поскольку в Set есть множество полезных функций, таких как 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;
 }, []);
sergeyz
источник
Можете ли вы объяснить, почему вы просто не pushиспользовали элемент в массиве вместо использования concat? Я попытался использовать толчок, и это не удалось. Я ищу объяснение.
Макенова
1
Причина в возвращаемом значении. concat возвращает измененный массив (именно то, что должно быть возвращено внутри функции Reduce), в то время как push возвращает индекс, по которому вы можете получить доступ к push-значению. Это отвечает на ваш вопрос?
сергейз
По-видимому, это решение довольно быстро в Chrome (и узел). 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];
chinmayan
источник
17

Этот прототип getUniqueне совсем корректен, потому что если у меня есть Array like: ["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 / underscore, вы можете просто создать 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, Firefox OS, Sencha, Phonegap, Titanium, ...).

Кер
источник
2
Тот факт, что это JS 1.6, не означает, что вы не можете использовать filter. На странице MDN есть реализация Internet Explorer, я имею в виду старые браузеры. Также: JS 1.6 относится только к js-движку Firefox, но правильно сказать, что это ECMAScript 5.
Камило Мартин,
@CamiloMartin Я изменил 1.6 на ECMAScript5. Спасибо.
Cœur
13

магия

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

O (N) производительность ; мы предполагаем, что ваш массив находится в aи t={}. Объяснение здесь (+ Jeppe impr.)

Камил Келчевски
источник
14
это выглядит так круто, что без веских объяснений я
упаду,
3
Я имел в виду, что вы должны расширить свой ответ некоторыми объяснениями и прокомментировать его разбор. не ожидайте, что люди найдут полезные ответы как это. (хотя это действительно здорово выглядит, вероятно, работает)
Ондржей Желязко
1
@Jeppe, когда я запускаю ваше улучшение, тогда я испытываю эффект ага (прежде чем я не знаю, что я могу использовать inоператор вне конструкции, кроме forцикла: P) - Спасибо - я ценю это и дам +2 к другим вашим хорошим ответам ,
Камиль Келчевски
2
разве это не создает глобальную переменную, tкоторая сохраняется после фильтрации ... ??
Филипп
1
Это действительно сложное решение. К сожалению, вы должны объявить t вне выражения, если вы не хотите определять глобальную переменную и / или полагаться на свой модуль-компоновщик, чтобы скрыть глобальную переменную. Это выглядит необычно, но сделайте себе (и своей команде разработчиков) услугу и просто напишите два строчки: let 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]
ifelse.codes
источник
Хотя этот код может решить вопрос, в том числе объяснение того, как и почему это решает проблему, действительно поможет улучшить качество вашего сообщения и, вероятно, получит больше голосов "за". Помните, что вы отвечаете на вопрос для читателей в будущем, а не только для того, кто спрашивает сейчас. Пожалуйста, отредактируйте свой ответ, чтобы добавить объяснение и указать, какие ограничения и предположения применяются.
id.ot
11

Если вы используете платформу Prototype, вам не нужно делать циклы 'for', вы можете использовать http://www.prototypejs.org/api/array/uniq следующим образом:

var a = Array.uniq();  

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

Uniq ()

я использовал

размер()

и был мой простой результат. ps Извините, если я что-то неправильно набрал

редактировать: если вы хотите избежать неопределенных записей, вы можете добавить

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

раньше, вот так:

var a = Array.compact().uniq();  
Decebal
источник
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
}
ephemient
источник
Я думаю, что это не будет работать, если массив содержит объекты / массивы, и я не уверен, сохранит ли он тип скаляров.
Камило Мартин
Да, все становится строгим. Это можно исправить, сохранив исходное значение oвместо просто a 1, хотя сравнение на равенство будет по-прежнему строковым (хотя из всех возможных равенств Javascript это не выглядит слишком необоснованным).
Эфимент
Array.prototype может быть расширен только не перечисляемыми методами .... Object.defineProperty (Array.prototype, "getUnique", {}) ... но идея использования вспомогательного объекта очень хороша
bortunac
10

У меня была немного другая проблема, когда мне нужно было удалить объекты с дублирующимися свойствами идентификатора из массива. это сработало.

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
источник
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 (или Underscore).

Применение

Если у вас его нет в базе кода, установите его с помощью npm:

npm install lodash

Затем используйте его следующим образом:

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

Вне:

[ 1, 2, 3 ]
NikeshPathania
источник
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);
Дейв
источник
Отличное решение, знайте, что новый массив вернется из функции. (это не
меняет
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]
Морозный Z
источник
6

Поиск уникальных значений Array простым методом

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 , но он пока недоступен во всех браузерах. Это экономит память и более эффективно, если вам нужно много раз просматривать его содержимое.

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]

Ле Дроид
источник