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

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

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

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

Дополнительные ответы от дублирующего вопроса:

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

Ответ 1

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

Ответ 2

Обновленный ответ для 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)]

Ответ 3

Я понимаю, что этот вопрос уже содержит более 30 ответов. Но сначала я прочитал все существующие ответы и сделал собственные исследования.

Я разделил все ответы на 4 возможных решения:

  • Используйте новую функцию ES6: [...new Set( [1, 1, 2] )];
  • Использовать объект { } для предотвращения дублирования
  • Использовать вспомогательный массив [ ]
  • Используйте 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 для кода.

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

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

Примечание: этот код был вытащен из Paul Irish duck punching post - Я забыл отдать должное: P

Ответ 7

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

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

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

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

Если вам интересно, что означает ..., он называется оператором распространения . Из MDN: "Оператор спрединга позволяет расширять выражение в местах, где ожидаются несколько аргументов (для вызовов функций) или нескольких элементов (для литералов массива)". Поскольку Set является итерируемым (и может иметь только уникальные значения), оператор с расширением будет расширять набор, чтобы заполнить массив.

Ресурсы для обучения ES6:

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

ТОЛЬКО ПРОИЗВОДИТЕЛЬНОСТЬ! этот код, вероятно, на 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]

Ответ 11

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

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

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

Array.from полезен для преобразования Set обратно в Array, чтобы у вас был легкий доступ ко всем удивительным методам (функциям), которые есть у массивов. Есть и другие способы сделать то же самое. Но вам может не понадобиться 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) становится тем, что мы назвали результатом, когда функция завершается.

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

Ответ 12

["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;
 }, []);

Ответ 13

Этот прототип 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"].

Ответ 14

Мы можем сделать это, используя наборы ES6:

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

console.log(uniqueArray);

Ответ 15

Без расширения 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 Store, Firefox OS, Sencha, Phonegap, Titanium,...).

Ответ 16

магия

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

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

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) ))

Ответ 17

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

var a = Array.uniq();  

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

Uniq()

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

размер()

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

edit: если вы хотите избежать записи undefined, вы можете добавить

compact()

раньше, например:

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

Ответ 18

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

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

Ответ 19

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
}

Ответ 20

[...new Set(duplicates)]

Это самый простой способ, на который ссылаются Документы MDN Web.

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]

Ответ 21

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

let objArr = [{
  id: '123'
}, {
  id: '123'
}, {
  id: '456'
}];

objArr = objArr.reduce((acc, cur) => [
  ...acc.filter((obj) => obj.id !== cur.id), cur
], []);

console.log(objArr);

Ответ 22

Из блог Шамаси Бхаттачарья (сложность времени 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]

Ответ 23

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

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
  )

Ответ 24

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

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]

Ответ 25

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

const uniqArray = array.filter((obj, idx, arr) => (
  arr.findIndex((o) => o.id === obj.id) === idx
)) 

Ответ 26

с es6 (и поддерживает порядок):

[...new Set(myArray)];

Ответ 27

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

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

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

Ответ 28

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

Ответ 29

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

Ответ 30

Сделайте набор массива, а затем инициализируйте мелкую копию набора в нужном контейнере.

let array = [1,2,3,2,1];
let uniqueArray = [... new Set(array)];