У меня есть массив чисел, которые я должен убедиться, что они уникальны. Я нашел фрагмент кода ниже в интернете, и он прекрасно работает, пока в массиве нет нуля. Я обнаружил этот другой скрипт здесь, в переполнении стека, который выглядит почти так же, как и он, но не дает сбоя.
Таким образом, ради того, чтобы помочь мне учиться, может ли кто-нибудь помочь мне определить, где прототип скрипта работает неправильно?
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
метода 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.
Это работает и для массивов со смешанными типами значений.
Для старых браузеров (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. Спасибо Лукасу Лиезе за подсказку в комментарии.
Многие из ответов здесь могут быть не полезны для начинающих. Если дедупликация массива затруднительна, они действительно будут знать о цепочке прототипов или даже 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 , когда функция завершается.
Просто передайте ему имя любого массива, который вам нравится.
Самое короткое решение с 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:
- Изучение ES6 доктора Акселя Раушмайера
- Выполните поиск «ES6» в еженедельных информационных бюллетенях JS.
- ES6 в подробных статьях из блога Mozilla Hacks
Я понимаю, что на этот вопрос уже есть более 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 Sheets, поэтому я не могу его протестировать.
Я ожидал увидеть, что код, использующий объект { }
, победит, потому что он использует хеш. Поэтому я рад, что тесты показали лучшие результаты для этого алгоритма в Chrome и IE. Спасибо @rab за код.
[...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]
Чтобы решить эту проблему с другой стороны, может быть полезно, чтобы при загрузке массива не было дубликатов, способ 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]
Самое простое решение:
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)));
С тех пор я нашел хороший метод, который использует jQuery
arr = $.grep(arr, function(v, k){
return $.inArray(v ,arr) === k;
});
Примечание. Этот код был взят из сообщения Пола Айриша о перфорации утки - я забыл дать кредит: P
Мы можем сделать это, используя наборы 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];
Теперь с помощью наборов вы можете удалять дубликаты и преобразовывать их обратно в массив.
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);
Я не уверен, почему Габриэль Сильвейра написал такую функцию, но более простая форма, которая работает для меня так же хорошо и без минимизации:
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
)
Магия
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) ))
Если вы используете платформу Prototype, вам не нужно делать циклы «for», вы можете использовать http: //www.prototypejs .org / api / array / uniq вот так:
var a = Array.uniq();
Который будет производить дубликат массива без дубликатов. Я наткнулся на ваш вопрос в поисках метода для подсчета различных записей массива, поэтому после
Uniq ( )
Я использовал
размер()
И был мой простой результат. постскриптум Извините, если я что-то опечатал
Редактировать: если вы хотите избежать неопределенных записей, вы можете добавить
компактный ( )
Раньше, вот так:
var a = Array.compact().uniq();
У меня была немного другая проблема, когда мне нужно было удалить объекты с повторяющимися свойствами 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);
Обновленный ответ для 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)]
На это было дано много ответов, но это не отвечало моим конкретным потребностям.
Многие ответы таковы:
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);
Странно, что раньше этого не предлагалось .. для удаления дубликатов по ключу объекта (id
ниже) в массиве вы можете сделать что-то вроде этого:
const uniqArray = array.filter((obj, idx, arr) => (
arr.findIndex((o) => o.id === obj.id) === idx
))
Похоже, мы потеряли ответ Рафаэля, который считался принятым ответом в течение нескольких лет. , Это было (по крайней мере, в 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;
}
Один лайнер, чистый 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 +
["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;
}, []);
Если у вас все в порядке с дополнительными зависимостями или у вас уже есть одна из библиотек в вашей кодовой базе, вы можете удалить дубликаты из массива на месте с помощью LoDash (или Underscore).
Использование
Если у вас его нет в базе кода, установите его с помощью npm:
npm install lodash
Затем используйте его следующим образом:
import _ from 'lodash';
let idArray = _.uniq ([
1,
2,
3,
3,
3
]);
console.dir(idArray);
Выход:
[ 1, 2, 3 ]
Из Шамасис Бхаттачарья ' Блог 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]
Без расширения 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, ...).
Вы также можете использовать underscore.js.
console.log(_.uniq([1, 2, 1, 3, 1, 4]));
<script src="http://underscorejs.org/underscore-min.js"></script>
Который вернет:
[1, 2, 3, 4]
Самый простой и самый быстрый (в 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.
Этот прототип 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"]
.
ТОЛЬКО ЭФФЕКТИВНОСТЬ! этот код, вероятно, в 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);
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
}
Поиск уникальных значений 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]
Это потому, что 0
является ложным значением в JavaScript.
this[i]
будет ложным, если значение массива равно 0 или любому другому ложному значению.
Похожие вопросы
Связанные вопросы
Новые вопросы
javascript
По вопросам программирования на ECMAScript (JavaScript/JS) и его различных диалектах/реализациях (кроме ActionScript). Обратите внимание, что JavaScript — это НЕ Java. Включите все теги, относящиеся к вашему вопросу: например, [node.js], [jQuery], [JSON], [ReactJS], [angular], [ember.js], [vue.js], [typescript], [стройный] и т. д.