Unique() для массивов в javascript

Как известно, нет встроенной функции для удаления дубликатов из массива в javascript. Я заметил, что этого также не хватает в jQuery (который имеет уникальную функцию только для выбора DOM), а наиболее распространенный фрагмент, который я нашел, проверяет весь массив и его подмножество для каждого элемента (не очень эффективно, я думаю), например

for (var i = 0; i < arr.length; i++)
    for (var j = i + 1; j < arr.length; j++)
        if (arr[i] === arr[j])
            //whatever

поэтому я сделал свой собственный:

function unique (arr) {
    var hash = {}, result = [];
    for (var i = 0; i < arr.length; i++)
        if (!(arr[i] in hash)) { //it works with objects! in FF, at least
            hash[arr[i]] = true;
            result.push(arr[i]);
        }
    return result;
}

Интересно, если какой-либо другой алгоритм принят как лучший для этого случая (или если вы видите какой-либо очевидный недостаток, который может быть исправлен) или, что вы будете делать, когда вам это нужно в javascript (я знаю, что jQuery не является единственной структурой, и некоторые другие могут уже охватить это).

Ответ 1

Использование литерала объекта - именно то, что я сделал бы. Многие люди часто пропускают эту технику, предпочитая вместо этого типичные массивные прогулки в качестве исходного кода, который вы показали. Единственной оптимизацией было бы избежать поиска arr.length каждый раз. Кроме этого, O (n) примерно так же хорош, как и для уникальности, и намного лучше, чем исходный пример O (n ^ 2).

function unique(arr) {
    var hash = {}, result = [];
    for ( var i = 0, l = arr.length; i < l; ++i ) {
        if ( !hash.hasOwnProperty(arr[i]) ) { //it works with objects! in FF, at least
            hash[ arr[i] ] = true;
            result.push(arr[i]);
        }
    }
    return result;
}

// * Edited to use hasOwnProperty per comments

Временные сложности для суммирования

  f()    | unsorted | sorted | objects | scalar | library
____________________________________________________________
unique   |   O(n)   |  O(n)  |   no    |  yes   |    n/a
original |  O(n^2)  | O(n^2) |   yes   |  yes   |    n/a
uniq     |  O(n^2)  |  O(n)  |   yes   |  yes   | Prototype
_.uniq   |  O(n^2)  |  O(n)  |   yes   |  yes   | Underscore

Как и в большинстве алгоритмов, есть компромиссы. Если вы только сортируете скалярные значения, вы вносите изменения в исходный алгоритм, предоставляете наиболее оптимальное решение. Однако, если вам нужно отсортировать нескалярные значения, то использование или подражание методу uniq любой из обсуждаемых библиотек будет вашим лучшим выбором.

Ответ 2

Я думаю, что ваша версия не будет работать, если у вас будут объекты или функции в массиве, которые предоставляют строковое представление, например [Object object]. Потому что вы можете иметь только строки в виде ключей в объектах (здесь здесь находится "хэш" ). Вам нужно будет зациклиться на массиве результатов, чтобы узнать, существует ли новая запись. Он по-прежнему будет быстрее первого метода.

Прототип JS имеет метод uniq", вы можете получить от него вдохновение.

Ответ 3

забава с весельем (ctional)

function uniqueNum(arr) {
    return Object.keys(arr.reduce(
        function(o, x) {o[x]=1; return o;}, {})).map(Number);
}  

Ответ 4

Я не специалист по алгоритму каким-либо образом, но я слежу за underscore.js. Они имеют это как функцию uniq:

http://documentcloud.github.com/underscore/#uniq

Я просмотрел код в своей библиотеке и скопировал его здесь для справки (не мой код, этот код принадлежит underscore.js):

// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
_.uniq = function(array, isSorted) {
    return _.reduce(array, [], function(memo, el, i) {
        if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo.push(el);
        return memo;
    });
};

РЕДАКТИРОВАТЬ: вам нужно пройти через весь код underscore.js, и я почти вынул этот код из-за этого. Я оставил фрагмент кода на всякий случай, если это все еще полезно.

Ответ 5

К сожалению, объекты JS не имеют идентификатора, доступного с языка, как упоминалось в других плакатах, использование объектов в качестве ключей в словаре не будет выполнено, если разные объекты имеют равные строковые представления, и на языке нет функции id().

Существует способ избежать проверки всех пар O (n ^ 2) для идентификатора ===, если вы можете изменить объекты. Выберите случайную строку, пройдите массив один раз, чтобы проверить, что ни один объект не имеет свойства по этому имени, а затем просто arr[i][randomPropertyName]=1 для каждого i. Если следующий объект в массиве уже имеет это свойство, то он является дубликатом.

К сожалению, вышесказанное будет работать только для изменяемых объектов. Он терпит неудачу для значений массива, которые не позволяют установить свойство (например, целые числа, 42['random']=1 просто не работают:()