Получить все уникальные значения в массиве JavaScript (удалить дубликаты)
У меня есть массив чисел, которые я должен убедиться, что они уникальны. Я нашел фрагмент кода ниже в интернете, и он прекрасно работает, пока в массиве нет нуля. Я нашел этот другой скрипт здесь на SO, который выглядит почти так же, как и он, но он не дает сбоя.
Таким образом, ради того, чтобы помочь мне учиться, может ли кто-нибудь помочь мне определить, в чем проблема с прототипом скрипта?
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;
}
Больше ответов от дублирующего вопроса:
Подобный вопрос:
140 ответов
С 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.
Это работает и для массивов со смешанными типами значений.
Для старых браузеров (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']
Спасибо Camilo Martin за подсказку в комментарии.
ES6 имеет родной объект Set
хранить уникальные значения. Чтобы получить массив с уникальными значениями, вы можете сделать это:
var myArray = ['a', 1, 'a', 2, '1'];
let unique = [...new Set(myArray)];
// unique is ['a', 1, 2, '1']
Конструктор Set
принимает итеративный объект, такой как Array, и оператор распространения ...
превратить набор обратно в массив. Спасибо Lukas за подсказку в комментарии.
Обновленный ответ для 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)]
Я понимаю, что на этот вопрос уже есть более 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 за код.
Вы также можете использовать http://underscorejs.org/.
console.log(_.uniq([1, 2, 1, 3, 1, 4]));
<script src="http://underscorejs.org/underscore-min.js"></script>
который вернется:
[1, 2, 3, 4]
Один лайнер, чистый 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+
Удалить дубликаты с помощью
Set
.
// Array with duplicates⤵️
const withDuplicates = [2, 2, 5, 5, 1, 1, 2, 2, 3, 3];
// Get new array without duplicates by using Set
// [2, 5, 1, 3]
const withoutDuplicates = Array.from(new Set(arrayWithDuplicates));
Многие из ответов здесь могут быть бесполезны для начинающих. Если дедупликация массива затруднительна, они действительно будут знать о цепочке прототипов или даже jQuery?
В современных браузерах чистое и простое решение - хранить данные в наборе, который представляет собой список уникальных значений.
const cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford'];
const uniqueCars = Array.from(new Set(cars));
Array.from
полезно преобразовать Set обратно в массив, чтобы иметь легкий доступ ко всем удивительным методам (функциям), которые есть у массивов. Есть и другие способы сделать то же самое. Но вам может не понадобиться Array.from
Вообще, у Set есть много полезных функций, таких как 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)
part становится тем, что мы назвали результатом, когда функция завершается.
Просто передайте ему имя любого массива, который вам нравится.
С тех пор я нашел хороший метод, который использует jQuery
arr = $.grep(arr, function(v, k){
return $.inArray(v ,arr) === k;
});
Примечание: этот код был взят из сообщения Уши Пола Айриша: я забыл отдать должное:P
Самое короткое решение с 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
магия
a.filter(( t={}, e=>!(t[e]=++t[e]|0) ))
O(N) производительность; мы предполагаем, что ваш массив находится в a
, Рабочий пример ЗДЕСЬ
Самое простое решение:
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;
}
или не прототип:
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.
Мы можем сделать это, используя наборы 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];
ТОЛЬКО ЭФФЕКТИВНОСТЬ! этот код, вероятно, в 10 раз быстрее, чем все приведенные здесь *, работает во всех браузерах, а также имеет минимальное влияние на память.... и многое другое
если вам не нужно повторно использовать старый массив, то, между прочим, выполните другие необходимые операции, прежде чем преобразовать его в уникальный, здесь, возможно, самый быстрый способ сделать это, также очень короткий.
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... немного быстрее и короче (в chrome в 2 раза быстрее);). Недостаточно!. unique3()
,
в этот момент я думал о том, что мне действительно нужно в МОЕЙ уникальной функции. мне не нужен старый массив, я хочу быструю функцию. поэтому я использовал 2 цикла + петли. unique4()
Бесполезно говорить, что я был впечатлен.
chrome: обычные 150000 операций в секунду подскочили до 1 800 000 операций в секунду.
то есть: 80 000 операций / с против 3 500 000 операций / с
IOS: 18 000 операций / с против 170 000 операций / с
сафари: 80 000 оп / с против 6 000 000 оп / с
Доказательство http://jsperf.com/wgu или лучше использовать console.time... microtime... что угодно
unique5()
просто чтобы показать вам, что происходит, если вы хотите сохранить старый массив.
Не использовать Array.prototype
если ты не знаешь, что делаешь. Я только что сделал много копий и прошлого. использование Object.defineProperty(Array.prototype,...,writable:false,enumerable:false})
если вы хотите создать собственный prototype.example: /questions/26337992/preobrazovanie-razmera-fajla-v-bajtah-v-udobochitaemuyu-stroku/26338023#26338023
Демо http://jsfiddle.net/46S7g/
ПРИМЕЧАНИЕ: ваш старый массив уничтожается / становится уникальным после этой операции.
Если вы не можете прочитать приведенный выше код, спросите, прочитайте книгу по javascript или вот несколько объяснений более короткого кода. /questions/32407889/kak-minimizirovat-kod-javascript/32407909#32407909
некоторые используют indexOf
... не... http://jsperf.com/dgfgghfghfghghgfhgfhfghfhgfh
для пустых массивов
!array.length||toUnique(array);
["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;
}, []);
На это было много ответов, но это не удовлетворило мою конкретную потребность.
Многие ответы такие:
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);
Изучив все 90+ ответов здесь, я увидел, что есть место для еще одного:
Array.includes имеет очень удобный второй параметр: fromIndex, поэтому при его использовании каждая итерация
filter
метод обратного вызова будет искать в массиве, начиная с
[current index] + 1
что гарантирует не включать в поиск отфильтрованный в данный момент элемент, а также экономит время.
//
var list = [0,1,2,2,3,'a','b',4,5,2,'a']
console.log(
list.filter((v,i) => !list.includes(v,i+1))
)
// [0,1,3,"b",4,5,2,"a"]
Пояснение:
Например, есть
2
с индексом 2, а раздел массива, который сканируется на наличие дубликатов, - это все, что находится после индекса 2:
[0, 1, 2, 2 ,3 ,'a', 'b', 4, 5, 2, 'a']
⬆|----------------------------|
А поскольку текущее значение фильтруемого элемента
2
включен в остальную часть массива, он будет отфильтрован из-за ведущего восклицательного знака, который отменяет правило фильтрации.
Вы можете просто использовать встроенные функции
Array.prototype.filter()
а также
Array.prototype.indexOf()
array.filter((x, y) => array.indexOf(x) == y)
Этот прототип 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"]
,
[...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]
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
}
Это потому что 0
является ложным значением в JavaScript.
this[i]
будет ложным, если значение массива равно 0 или любое другое ложное значение.
Без расширения 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, ...).
Если вы используете платформу Prototype, вам не нужно делать циклы 'for', вы можете использовать http://www.prototypejs.org/api/array/uniq следующим образом:
var a = Array.uniq();
Который будет производить дубликат массива без дубликатов. Я наткнулся на ваш вопрос в поисках метода для подсчета различных записей массива, поэтому после
Uniq()
я использовал
размер()
и был мой простой результат. ps Извините, если я что-то неправильно набрал
редактировать: если вы хотите избежать неопределенных записей, вы можете добавить
компактный ()
раньше, вот так:
var a = Array.compact().uniq();
Теперь с помощью наборов вы можете удалять дубликаты и преобразовывать их обратно в массив.
var names = ["Mike","Matt","Nancy", "Matt","Adam","Jenny","Nancy","Carl"];
console.log([...new Set(names)])
Вы можете использовать оператор Set для получения уникальных значений из массива
const uniqueArray = [...new Set([1,1,1])]
console.log(uniqueArray) // [1]
Самый простой ответ:
const array = [1, 1, 2, 2, 3, 5, 5, 2];
const uniqueArray = [...new Set(array)];
console.log(uniqueArray); // [1, 2, 3, 5]
У меня была немного другая проблема, когда мне нужно было удалить объекты с дублирующимися свойствами идентификатора из массива. это сработало..
let objArr = [ { id: '123' }, { id: '123' }, { id: '456' } ];
objArr = objArr.reduce( ( acc, cur ) => [
...acc.filter( ( obj ) => obj.id !== cur.id ), cur
], [] );