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

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

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

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

больше ответов от дубликатов вопрос:

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

30 ответов


С 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 by 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, и оператор spread ... преобразовать набор обратно в массив. Спасибо Лукаш Лизе для подсказки в комментарии.


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

As le_m предложил, это также может быть сокращен с помощью распространение оператор , как

var uniqueItems = [...new Set(items)]

вы также можете использовать подчеркивания.js.

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

что вернется:

[1, 2, 3, 4]

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

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

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


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

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

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


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


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

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

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

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

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

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


простое решение:

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

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

или non-прототипа:

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.


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

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

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-loop. хотя это самый быстрый цикл во всех браузерах, кроме того, который нам так нравится... хром.

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

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

в этот момент я думал о том, что мне действительно нужно в моей уникальной функции. мне не нужен старый массив, мне нужна быстрая функция. поэтому я использовал 2 while loops + splice.unique4()

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

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

ie: 80,000 op / s против 3,500,000 op / s

ios: 18,000 op / s против 170,000 op / s

сафари: 80,000 op / s против 6,000,000 op / s

доказательство http://jsperf.com/wgu или лучше использовать консоль.время... микротайм... все

unique5() просто показать вам, что происходит, если вы хотите сохранить старый массив.

не используйте Array.prototype если вы не знаете, что делаете. я просто сделал много копий и прошлого. Использовать Object.defineProperty(Array.prototype,...,writable:false,enumerable:false}) если вы хотите создать собственный прототип.пример: https://stackoverflow.com/a/20463021/2450730

демо http://jsfiddle.net/46S7g/

Примечание: ваш старый массив уничтожено/becomestheunique после этой операции.

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

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

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

!array.length||toUnique(array); 

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

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

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

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

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


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


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

без расширения массива.прототип (это, как говорят, плохая практика) или использование 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 App, Firefox OS, Sencha, Phonegap, Titanium, ...).


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

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


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
}

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

var a = Array.uniq();  

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

uniq ()

Я

размер()

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

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

compact ()

раньше, вроде этого:

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

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

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
  )

С Shamasis Бхаттачарияблог с (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]

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

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]

чтобы решить проблему наоборот, может быть полезно не иметь дубликатов во время загрузки массива, путь Set object сделает это, но он еще не доступен во всех браузерах. Он экономит память и более эффективен, если вам нужно много раз смотреть на его содержимое.

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]


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

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

мы можем сделать это с помощью наборов ES6:

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

/ / выход будет

uniqueArray = [1,2,3,4,5];

Если кто-нибудь использует knockoutjs

ko.utils.arrayGetDistinctValues()

кстати, посмотрите на всех ko.utils.array* служебные программы.


Я обнаружил, что сериализация хэш-ключа помогла мне заставить это работать для объектов.

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

вы также можете использовать сахар.js:

[1,2,2,3,1].unique() // => [1,2,3]

[{id:5, name:"Jay"}, {id:6, name:"Jay"}, {id: 5, name:"Jay"}].unique('id') 
  // => [{id:5, name:"Jay"}, {id:6, name:"Jay"}]

вы также можете использовать jQuery

var a = [1,5,1,6,4,5,2,5,4,3,1,2,6,6,3,3,2,4];

// note: jQuery's filter params are opposite of javascript's native implementation :(
var unique = $.makeArray($(a).filter(function(i,itm){ 
    // note: 'index', not 'indexOf'
    return i == $(a).index(itm);
}));

// unique: [1, 5, 6, 4, 2, 3]

первоначально ответил на:функция jQuery для получения всех уникальных элементов из массива?


Это будет работать.

function getUnique(a) {
  var b = [a[0]], i, j, tmp;
  for (i = 1; i < a.length; i++) {
    tmp = 1;
    for (j = 0; j < b.length; j++) {
      if (a[i] == b[j]) {
        tmp = 0;
        break;
      }
    }
    if (tmp) {
      b.push(a[i]);
    }
  }
  return b;
}

основываясь на других ответах, вот еще один вариант, который принимает необязательный флаг для выбора стратегии (сохранить первое вхождение или сохранить последнее):

без продления Array.prototype

function unique(arr, keepLast) {
  return arr.filter(function (value, index, array) {
    return keepLast ? array.indexOf(value, index + 1) < 0 : array.indexOf(value) === index;
  });
};

// Usage
unique(['a', 1, 2, '1', 1, 3, 2, 6]); // -> ['a', 1, 2, '1', 3, 6]
unique(['a', 1, 2, '1', 1, 3, 2, 6], true); // -> ['a', '1', 1, 3, 2, 6]

расширения Array.prototype

Array.prototype.unique = function (keepLast) {
  return this.filter(function (value, index, array) {
    return keepLast ? array.indexOf(value, index + 1) < 0 : array.indexOf(value) === index;
  });
};

// Usage
['a', 1, 2, '1', 1, 3, 2, 6].unique(); // -> ['a', 1, 2, '1', 3, 6]
['a', 1, 2, '1', 1, 3, 2, 6].unique(true); // -> ['a', '1', 1, 3, 2, 6]

используя объектные ключи для создания уникального массива, я попытался следовать

function uniqueArray( ar ) {
  var j = {};

  ar.forEach( function(v) {
    j[v+ '::' + typeof v] = v;
  });


  return Object.keys(j).map(function(v){
    return j[v];
  });
}   

uniqueArray(["1",1,2,3,4,1,"foo", false, false, null,1]);

возвращает ["1", 1, 2, 3, 4, "foo", false, null]