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

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

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

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

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

1407
Mottie 25 Дек 2009 в 07:28

30 ответов

Лучший ответ

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

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

Для старых браузеров (filter и indexOf, вы можете найти обходные пути в документации MDN для filter и 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. Спасибо Лукасу Лиезе за подсказку в комментарии.

2535
Thomas Ruiz 1 Авг 2018 в 15:17

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

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

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) часть становится тем, что мы назвали result , когда функция завершается.

Просто передайте ему имя любого массива, который вам нравится.

27
Community 23 Май 2017 в 11:55

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

51
gregers 18 Авг 2015 в 13:21

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

162
Community 23 Май 2017 в 11:55
[...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]
13
ifelse.codes 20 Июл 2019 в 20:13

Чтобы решить эту проблему с другой стороны, может быть полезно, чтобы при загрузке массива не было дубликатов, способ 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]

5
Le Droid 27 Янв 2016 в 20:52

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

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)));
35
Ruslan López 23 Мар 2017 в 01:09

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

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

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

51
Mottie 18 Дек 2012 в 14:27

Мы можем сделать это, используя наборы 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];
18
demo 22 Мар 2019 в 16:04

Теперь с помощью наборов вы можете удалять дубликаты и преобразовывать их обратно в массив.

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);
11
Krishnadas PC 24 Окт 2019 в 08:09

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

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
Dan Fox 7 Июн 2013 в 03:30

Магия

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

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

let t={}, unique= a=> a.filter(e=>!(t[e]=e in t));

// "stand-alone" version working with global t:
// a1.filter((t={},e=>!(t[e]=e in t)));

// Test data
let a1 = [5,6,0,4,9,2,3,5,0,3,4,1,5,4,9];
let a2 = [[2, 17], [2, 17], [2, 17], [1, 12], [5, 9], [1, 12], [6, 2], [1, 12]];
let a3 = ['Mike', 'Adam','Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl'];

// Results
console.log(JSON.stringify( unique(a1) ))
console.log(JSON.stringify( unique(a2) ))
console.log(JSON.stringify( unique(a3) ))
13
Kamil Kiełczewski 31 Май 2019 в 13:47

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

var a = Array.uniq();  

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

Uniq ( )

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

размер()

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

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

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

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

var a = Array.compact().uniq();  
11
Decebal 1 Ноя 2011 в 13:26

У меня была немного другая проблема, когда мне нужно было удалить объекты с повторяющимися свойствами 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);
10
demo 22 Мар 2019 в 16:03

Обновленный ответ для 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)]
834
A.T. 14 Ноя 2017 в 10:57

На это было дано много ответов, но это не отвечало моим конкретным потребностям.

Многие ответы таковы:

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);
7
Dave 1 Окт 2019 в 10:42

Странно, что раньше этого не предлагалось .. для удаления дубликатов по ключу объекта (id ниже) в массиве вы можете сделать что-то вроде этого:

const uniqArray = array.filter((obj, idx, arr) => (
  arr.findIndex((o) => o.id === obj.id) === idx
)) 
5
daviestar 1 Фев 2018 в 16:36

Похоже, мы потеряли ответ Рафаэля, который считался принятым ответом в течение нескольких лет. , Это было (по крайней мере, в 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;
}

Если у вас do есть массив смешанного типа, вы можете сериализовать ключ хеша:

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;
}
6
Adam Katz 11 Сен 2019 в 21:15

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

66
Vamsi 13 Сен 2016 в 06:14
["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;
 }, []);
20
sergeyz 19 Сен 2014 в 19:09

Если у вас все в порядке с дополнительными зависимостями или у вас уже есть одна из библиотек в вашей кодовой базе, вы можете удалить дубликаты из массива на месте с помощью LoDash (или Underscore).

Использование

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

npm install lodash

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

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

Выход:

[ 1, 2, 3 ]
7
Constant Meiring 21 Июн 2018 в 13:29

Из Шамасис Бхаттачарья ' Блог 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 enjveX0}}:

(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
Frosty Z 18 Дек 2012 в 14:14

Без расширения 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, ...).

13
Cœur 23 Май 2013 в 14:21

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

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

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

[1, 2, 3, 4]
132
Ruslan López 23 Мар 2017 в 04:00

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

32
Joeytje50 30 Янв 2014 в 01:00

Этот прототип getUnique не совсем корректен, потому что если у меня есть Array вроде: ["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"].

17
Gajus 10 Апр 2014 в 10:26

ТОЛЬКО ЭФФЕКТИВНОСТЬ! этот код, вероятно, в 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 ... немного быстрее и короче (в хроме в 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 операций / с

< Сильный > Proof 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

< Сильный > Demo http://jsfiddle.net/46S7g/

ПРИМЕЧАНИЕ. Ваш старый массив после этой операции уничтожается / становится уникальным.

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

Некоторые используют indexOf ... не ... http://jsperf.com/dgfgghfghfghghgfhgfhfghfhgfh

Для пустых массивов

!array.length||toUnique(array); 
30
Community 23 Май 2017 в 11:47
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
}
10
ephemient 25 Дек 2009 в 05:11

Поиск уникальных значений 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
Saravanan Rajaraman 7 Янв 2016 в 13:39

Это потому, что 0 является ложным значением в JavaScript.

this[i] будет ложным, если значение массива равно 0 или любому другому ложному значению.

10
Luca Matteis 25 Дек 2009 в 04:32