Получить все уникальные значения в массиве JavaScript (удалить дубликаты) - PullRequest
1123 голосов
/ 25 декабря 2009

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

Итак, ради того, чтобы помочь мне учиться, может ли кто-нибудь помочь мне определить, где происходит ошибка прототипа сценария?

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

Больше ответов на повторяющийся вопрос:

Аналогичный вопрос:

Ответы [ 70 ]

2085 голосов
/ 21 января 2013

С 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.

Работает и для массивов со смешанными типами значений.

Для старых браузеров (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, а оператор распространения ... преобразует набор обратно в Array. Спасибо Lukas Liese за подсказку в комментарии.

699 голосов
/ 14 октября 2015

Обновленный ответ для 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)]
127 голосов
/ 11 июля 2012

Вы также можете использовать underscore.js .

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

который вернет:

[1, 2, 3, 4]
126 голосов
/ 27 марта 2017

Я понимаю, что на этот вопрос уже есть более 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, поэтому я не могу его протестировать.

Вот результат тестов: enter image description here

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

56 голосов
/ 01 сентября 2016

Один лайнер, чистый 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")

enter image description here

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

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

Совместимость браузера : IE9 +

50 голосов
/ 12 июля 2012

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

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

Примечание: этот код был извлечен из Удара Пола Айриша: - Я забыл дать кредит: P

46 голосов
/ 23 апреля 2014

Самое короткое решение с 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:

34 голосов
/ 15 марта 2016

Самое простое решение:

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)));
31 голосов
/ 30 января 2014

Самый простой и самый быстрый (в 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.

29 голосов
/ 01 августа 2014

ТОЛЬКО ЭФФЕКТИВНОСТЬ! этот код, вероятно, в 10 раз быстрее, чем все приведенные здесь коды *, работает во всех браузерах и также имеет минимальное влияние на память .... и больше

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

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 ... немного быстрее и короче (в хроме в 2 раза быстрее);). Не достаточно!. unique3().

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

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

chrome: обычные 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); 
...