Получить все уникальные значения в массиве 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 ответов
Если у вас все в порядке с дополнительными зависимостями или у вас уже есть одна из библиотек в вашей кодовой базе, вы можете удалить дубликаты из массива с помощью LoDash (или Underscore).
использование
Если у вас его нет в базе кода, установите его с помощью npm:
npm install lodash
Затем используйте его следующим образом:
import _ from 'lodash';
let idArray = _.uniq ([
1,
2,
3,
3,
3
]);
console.dir(idArray);
Из:
[ 1, 2, 3 ]
В ES6/позже
Получить только уникальные значения
let a = [
{ id: 1, name: "usman" },
{ id: 2, name: "zia" },
{ id: 3, name: "usman" },
];
const unique = [...new Set(a.map((item) => item.name))];
console.log(unique); // ["usman", "zia"]
Получите уникальные объекты
const myObjArray = [
{ id: 1, name: "usman" },
{ id: 2, name: "zia" },
{ id: 3, name: "usman" },
];
// Creates an array of objects with unique "name" property values.
let uniqueObjArray = [
...new Map(myObjArray.map((item) => [item["name"], item])).values(),
];
console.log("uniqueObjArray", uniqueObjArray);
Из блога Шамасиса Бхаттачарьи (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]
Я не уверен, почему Габриэль Сильвейра написал эту функцию, но более простая форма, которая работает для меня так же хорошо и без минимизации:
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
)
Я обнаружил, что сериализация их хеш-ключа помогла мне заставить это работать для объектов.
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;
}
Поиск уникальных значений 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]
Странно, это не было предложено раньше.. чтобы удалить дубликаты по ключу объекта (id
ниже) в массиве вы можете сделать что-то вроде этого:
const uniqArray = array.filter((obj, idx, arr) => (
arr.findIndex((o) => o.id === obj.id) === idx
))
Для объектно-ориентированного массива с некоторыми уникальными идентификаторами у меня есть простое решение, с помощью которого вы можете сортировать по линейной сложности
function getUniqueArr(arr){
const mapObj = {};
arr.forEach(a => {
mapObj[a.id] = a
})
return Object.values(mapObj);
}
Создайте набор из массива и затем инициализируйте мелкую копию набора в желаемом контейнере.
let array = [1,2,3,2,1];
let uniqueArray = [... new Set(array)];
Задача - получить уникальный массив из массива, состоящего из произвольных типов (примитивных и не примитивных).
Подход, основанный на использовании new Map(...)
не ново. Здесь это используетсяJSON.stringify(...)
, JSON.parse(...)
а также [].map
метод. Преимущества - универсальность (применимость для массива любых типов), короткая нотация ES6 и, вероятно, производительность в этом случае:
const dedupExample = [
{ a: 1 },
{ a: 1 },
[ 1, 2 ],
[ 1, 2 ],
1,
1,
'1',
'1'
]
const getUniqArr = arr => {
const arrStr = arr.map(item => JSON.stringify(item))
return [...new Set(arrStr)]
.map(item => JSON.parse(item))
}
console.info(getUniqArr(dedupExample))
/* [ {a: 1}, [1, 2], 1, '1' ] */
Вы также можете использовать метод Array.from для преобразования Set в Array, если принятый ответ не будет работать на Typescript.
если вы получите сообщение об ошибке:
Type 'Set<any>' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators.ts(2569)
let uniques = Array.from(new Set([1, 2, 3, 1, 1])) ;
console.log(uniques);
Только одна строка кода:
var arr = [1, 1, 2, 3] // example
var unique = new Set(arr) // 1, 2, 3
console.log(unique) // 1, 2, 3 when I run in node.js
Чтобы решить эту проблему с другой стороны, может быть полезно не иметь дубликатов при загрузке массива, способ, которым объект 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]
Если кто-то использует http://knockoutjs.com/
ko.utils.arrayGetDistinctValues()
Кстати, посмотреть на все ko.utils.array*
коммунальные услуги.
Уменьшите это!!!
Эта альтернатива вместо явной дедупликации возьмет массив и уменьшит его так, чтобы каждое значение массива можно было повторять и деструктурировать в накопительном поведении, игнорируя уже включенные значения, используя постоянство массива из-за рекурсивности.
['a', 1, 'a', 2, '1'].reduce((accumulator, currentValue) => accumulator.includes(currentValue) ? accumulator : [...accumulator, currentValue], [])
Пример теста:
var array = ['a', 1, 'a', 2, '1'];
const reducer = (accumulator, currentValue) => accumulator.includes(currentValue) ? accumulator : [...accumulator, currentValue];
console.log(
array.reduce(reducer, [])
);
Вывод
Намного более элегантный и полезный, когда надоедает for-each
подход хочет избежать (не то чтобы он бесполезен).
Нет необходимости во внешних библиотеках, таких как Underscore.js, JQuery или Lo-Dash, и нет проблем с созданием какой-либо встроенной функции для достижения желаемого эффекта дедупликации.
О, и ЭЙ!, это можно сделать однострочно!!!
Этот ответ был возможен благодаря ES5
(ECMAScript 2015) include()
а также reduce()
.
Используя ключи объекта для создания уникального массива, я попробовал следующее
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]
Вы также можете использовать 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 функцию, чтобы получить все уникальные элементы из массива?
Самый простой способ найти уникальный элемент с помощью метода фильтрации:
var A1 = [2,2,4,5,5,6,8,8,9];
var uniqueA1 = A1.filter(function(element){
return A1.indexOf(element) == A1.lastIndexOf(element);
});
console.log(uniqueA1);
Здесь логика состоит в том, что первое и последнее вхождение элемента одинаковы, тогда этот элемент встречается только один раз, поэтому элементы массива, которые передают условие обратного вызова, включаются в новый массив и отображаются.
Output: 4,6,9
Поиск уникальных объектов в массиве с помощью One Liner
const uniqueBy = (x,f)=>Object.values(x.reduce((a,b)=>((a[f(b)]=b),a),{}));
// f -> should must return string because it will be use as key
const data = [
{ comment: "abc", forItem: 1, inModule: 1 },
{ comment: "abc", forItem: 1, inModule: 1 },
{ comment: "xyz", forItem: 1, inModule: 2 },
{ comment: "xyz", forItem: 1, inModule: 2 },
];
uniqueBy(data, (x) => x.forItem +'-'+ x.inModule); // find unique by item with module
// output
// [
// { comment: "abc", forItem: 1, inModule: 1 },
// { comment: "xyz", forItem: 1, inModule: 2 },
// ];
// can also use for strings and number or other primitive values
uniqueBy([1, 2, 2, 1], (v) => v); // [1, 2]
uniqueBy(["a", "b", "a"], (v) => v); // ['a', 'b']
uniqueBy(
[
{ id: 1, name: "abc" },
{ id: 2, name: "xyz" },
{ id: 1, name: "abc" },
],
(v) => v.id
);
// output
// [
// { id: 1, name: "abc" },
// { id: 2, name: "xyz" },
// ];
Есть простой способ решить эту задачу через ES6 - используя Set:
let arr = [1, 1, 2, 2, 3, 3];
let deduped = [...new Set(arr)] // [1, 2, 3]
2021: Тест производительности для Node.js v12.14 и v14.15
Я взял основные алгоритмы с первой страницы ответов и проверил некоторые показатели производительности в Node.js v12, которая является моей целевой платформой. (Результаты были аналогичными в Node.js v14.15).
Мой код:
var uniq1 = function(ar) {
var item, j, len1, retar;
retar = [];
for (j = 0, len1 = ar.length; j < len1; j++) {
item = ar[j];
if (retar.indexOf(item) === -1 && item !== '') {
retar.push(item);
}
}
return retar;
};
var uniq2 = function(ar) {
return [...new Set(ar)];
};
var uniq3 = function(ar, b, c) {
b = ar.length;
while (c = --b) {
while (c--) {
ar[b] !== ar[c] || ar.splice(c, 1);
}
}
return ar;
};
var uniq4 = function(ar) {
var onlyUnique;
onlyUnique = function(value, index, self) {
return self.indexOf(value) === index;
};
return ar.filter(onlyUnique);
};
var uniq5 = function(ar) {
// lodash 4.5 uniq function
return _.uniq(ar);
};
Приложение, которое вызовет эту функцию, будет иметь около 50 значений с примерно 15 уникальными посетителями. Я сгенерировал несколько случайных чисел, подходящих для этого случая:
ar = [10,9,9,4,9,4,10,13,9,12,4,1,4,0,7,8,13,12,5,14,8,0,14,14,2,4,13,4,9,10,2,13,1,4,11,1,14,11,2,1,2,1,4,4,11,2,11,13,8,8]
Затем я запускал каждый тестовый пример 40000 раз на MacBook Pro c2015 г., и весь набор тестов был запущен несколько раз. Хотя затраченное время немного варьировалось между запусками, приведенные ниже числа являются типичными:
✔ uniq1: 39 ms
✔ uniq2: 51 ms
✔ uniq3: 19 ms
✔ uniq4: 67 ms
✔ (lodash) uniq5: 24 ms
Uniq3 - явный фаворит. В
lodash
результаты были очень похожи на, и я думаю, что прошедшее время немного медленнее связано с тем, что я заключил функцию lodash в свою функцию. Lodash, вероятно, использует тот же алгоритм, что и
uniq3
.
Дополнительные сведения о каждом из этих алгоритмов см. На первой странице ответов на этот вопрос.
Как уже объяснялось,
[...new Set(values)]
- лучший вариант, если он вам доступен.
В противном случае вот однострочник, который не выполняет итерацию массива для каждого индекса:
values.sort().filter((val, index, arr) => index === 0 ? true : val !== arr[index - 1]);
Это просто сравнивает каждое значение с предыдущим. Результат будет отсортирован.
Как насчет использования набора?
let productPrice = [230,560,125,230,678,45,230,125,127];
let tempData = new Set(productPrice);
let uniqeProductPrice = [...tempData];
uniqeProductPrice.forEach((item)=>{
console.log(item)
});
Найден этот сладкий отрывок из поста по Changhui Xu для тех, кто хочет получить уникальные объекты. Однако я не сравнивал его производительность с другими альтернативами.
const array = [{
name: 'Joe',
age: 17
},
{
name: 'Bob',
age: 17
},
{
name: 'Tom',
age: 25
},
{
name: 'John',
age: 22
},
{
name: 'Jane',
age: 20
},
];
const distinctAges = [...new Set(array.map(a => a.age))];
console.log(distinctAges)
Это будет работать
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;
}
Вот почти однострочник, который является O(n), сохраняет первый элемент и где вы можете сохранить поле, которое вы uniq'ing, отдельно.
Это довольно распространенный прием в функциональном программировании - вы используете
reduce
чтобы создать массив, который вы возвращаете. Поскольку мы строим массив таким образом, мы гарантируем, что получим стабильный порядок, в отличие от
[...new Set(array)]
подход. Мы по-прежнему используем a, чтобы убедиться, что у нас нет дубликатов, поэтому наш аккумулятор содержит как
Set
и массив, который мы строим.
Для пользователей TS!
const uniq = <T>(arr: T[]) => [...new Set(arr)];
Посмотри на это. Jquery предоставляет метод uniq: https://api.jquery.com/jQuery.unique/
var ids_array = []
$.each($(my_elements), function(index, el) {
var id = $(this).attr("id")
ids_array.push(id)
});
var clean_ids_array = jQuery.unique(ids_array)
$.each(clean_ids_array, function(index, id) {
elment = $("#" + id) // my uniq element
// TODO WITH MY ELEMENT
});
Вы можете использовать набор для устранения дубликатов.
const originalNumbers = [1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 1, 2, 9];
const uniqueNumbersSet = new Set(originalNumbers);
/** get the array back from the set */
const uniqueNumbersArray = Array.from(uniqueNumbersSet);
/** uniqueNumbersArray outputs to: [1, 2, 3, 4, 5, 9] */
Узнайте больше о наборе: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set .