Получить все уникальные значения в массиве 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 ответов
Самый простой способ - преобразовать значения в строки для фильтрации значений вложенных объектов.
const uniq = (arg = []) => {
const stringifyedArg = arg.map(value => JSON.stringify(value))
return arg.filter((value, index, self) => {
if (typeof value === 'object')
return stringifyedArg.indexOf(JSON.stringify(value)) === index
return self.indexOf(value) === index
})
}
console.log(uniq([21, 'twenty one', 21])) // [21, 'twenty one']
console.log(uniq([{ a: 21 }, { a: 'twenty one' }, { a: 21 }])) // [{a: 21}, {a: 'twenty one'}]
Вам вообще не нужен.indexOf(); вы можете сделать это O(n):
function SelectDistinct(array) {
var seenIt = {};
return array.filter(function (val) {
if (seenIt[val])
return false;
return seenIt[val] = true;
});
}
var hasDuplicates = [1,2,3,4,5,5,6,7,7];
console.log(SelectDistinct(hasDuplicates)) //[1,2,3,4,5,6,7]
Если вы не хотите использовать.filter():
function SelectDistinct(array) {
var seenIt = {};
var distinct = [];
for (var i in array) {
if (!seenIt[array[i]]) {
seenIt[array[i]] = true;
distinct.push(array[i]);
}
}
return distinct;
}
Версия, которая принимает селектор, должна быть довольно быстрой и лаконичной:
function unique(xs, f) {
var seen = {};
return xs.filter(function(x) {
var fx = (f && f(x)) || x;
return !seen[fx] && (seen[fx] = 1);
});
}
Array .forEach для обхода каждого элемента через массив и проверки условия if, если новый массив уже содержит элемент. Надеюсь это поможет!!
var arr = [1, 2, 2, 3, 4, 5, 6, 7, 7, 7, 7, 8, 9, 4, 3, 3, 5];
let arr1 = [];
arr.forEach(elem => {
if (!arr1.includes(elem)) {
arr1.push(elem);
}
});
console.log(arr1);
Многие люди уже упоминали об использовании...
[...new Set(arr)];
И это отличное решение, но я предпочитаю решение, которое работает с .filter
. На мой взгляд, фильтр - более естественный способ получить уникальные значения. Вы эффективно удаляете дубликаты, а удаление элементов из массива - это именно то, для чего предназначен фильтр. Это также позволяет вам цепляться за.map
, .reduce
и другие .filter
звонки. Я придумал это решение...
const unique = () => {
let cache;
return (elem, index, array) => {
if (!cache) cache = new Set(array);
return cache.delete(elem);
};
};
myArray.filter(unique());
Предостережение в том, что вам нужно закрытие, но я думаю, что это достойный компромисс. С точки зрения производительности, это более производительно, чем другие решения, которые я видел, которые используют.filter
, но хуже, чем [...new Set(arr)]
.
См. Также мой пакет github youneek
Это функция ES6, которая удаляет дубликаты из массива объектов, фильтруя по указанному свойству объекта
function dedupe(arr = [], fnCheck = _ => _) {
const set = new Set();
let len = arr.length;
for (let i = 0; i < len; i++) {
const primitive = fnCheck(arr[i]);
if (set.has(primitive)) {
// duplicate, cut it
arr.splice(i, 1);
i--;
len--;
} else {
// new item, add it
set.add(primitive);
}
}
return arr;
}
const test = [
{video:{slug: "a"}},
{video:{slug: "a"}},
{video:{slug: "b"}},
{video:{slug: "c"}},
{video:{slug: "c"}}
]
console.log(dedupe(test, x => x.video.slug));
// [{video:{slug: "a"}}, {video:{slug: "b"}}, {video:{slug: "c"}}]
Еще одно решение для кучи.
Недавно мне нужно было сделать отсортированный список уникальным, и я сделал это с помощью фильтра, который отслеживает предыдущий элемент в объекте, подобном этому:
uniqueArray = sortedArray.filter(function(e) {
if(e==this.last)
return false;
this.last=e; return true;
},{last:null});
Это один из подходов с использованием карты сокращения.
const arr = [{id: '1'},{id: '4'},{id: '2'},{id: '1'},{id: '3'},{id: '1'},{id: '1'},{id: '5'}]
let uniqueArr = arr.reduce((arr, item) => {
const uniq = arr.filter(i => i.id !== item.id)
return [...uniq, item]
}, [])
Я знаю, что на этот вопрос уже ответили до смерти... но... никто не упомянул реализацию linq на javascript. Тогда .distinct()
метод может быть использован - и это делает код очень легко читаемым.
var Linq = require('linq-es2015');
var distinctValues = Linq.asEnumerable(testValues)
.Select(x)
.distinct()
.toArray();
var testValues = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 1];
var distinctValues = Enumerable.asEnumerable(testValues)
.distinct()
.toArray();
console.log(distinctValues);
<script src="https://npmcdn.com/linq-es5/dist/linq.js"></script>
Когда вы говорите уникальные значения, для меня это означает значения, которые появляются один раз и только один раз в наборе данных.
Следующее фильтрует массив значений, проверяя, что первый и последний индексы данного значения равны. Если индекс равен, это означает, что значение должно появиться только один раз.
var values = [1, 2, 3, 4, 5, 2, 4, 6, 2, 1, 5];
var unique = values.filter(function(value) {
return values.indexOf(value) === values.lastIndexOf(value);
});
console.log(unique); // [3, 6]
Основываясь на отзывах, которые я неправильно понял, вот альтернативный подход, который возвращает уникальные, не повторяющиеся значения из массива значений.
var values = [1, 2, 3, 4, 5, 2, 4, 6, 2, 1, 5];
var unique = values.reduce(function(unique, value) {
return unique.indexOf(value) === -1 ? unique.concat([value]) : unique;
}, []);
console.log(unique); // [1, 2, 3, 4, 5, 6]
Я использую этот экстремально простой, и я почти уверен, что он совместим со всеми браузерами, но в современных браузерах он устарел:
Для массива строк:
function removeDuplicatesFromArray(arr) {
const unique = {};
arr.forEach((word) => {
unique[word] = 1; // it doesn't really matter what goes here
});
return Object.keys(unique);
}
var numbers = [1,1,2,3,4,4];
function unique(dupArray) {
return dupArray.reduce(function (previous, num){
if (previous.find(function(item){
return item == num;
})) {
return previous;
} else {
previous.push(num);
return previous;
}
}, [])
}
var check = unique(numbers);
console.log(check);
Для массива кортежей я добавляю вещи в карту и позволяю ей делать свою работу. При таком подходе вы должны помнить о ключе, который хотите использовать:
const arrayOfArraysWithDuplicates = [
[1, 'AB'],
[2, 'CD'],
[3, 'EF'],
[1, 'AB'],
[2, 'CD'],
[3, 'EF'],
[3, 'GH'],
]
const uniqueByFirstValue = new Map();
const uniqueBySecondValue = new Map();
arrayOfArraysWithDuplicates.forEach((item) => {
uniqueByFirstValue.set(item[0], item[1]);
uniqueBySecondValue.set(item[1], item[0]);
});
let uniqueList = Array.from( uniqueByFirstValue, ( [ value, name ] ) => ( [value, name] ) );
console.log('Unique by first value:');
console.log(uniqueList);
uniqueList = Array.from( uniqueBySecondValue, ( [ value, name ] ) => ( [value, name] ) );
console.log('Unique by second value:');
console.log(uniqueList);
Выход:
Unique by first value:
[ [ 1, 'AB' ], [ 2, 'CD' ], [ 3, 'GH' ] ]
Unique by second value:
[ [ 'AB', 1 ], [ 'CD', 2 ], [ 'EF', 3 ], [ 'GH', 3 ] ]
Всегда помните, встроенные методы просты в использовании. Но имейте в виду, что они имеют сложность.
Базовая логика лучше всего. Нет скрытой сложности.
let list = [1, 1, 2, 100, 2] // your array
let check = {}
list = list.filter(item => {
if(!check[item]) {
check[item] = true
return true;
}
})
или используйте, let check = [] если вам нужно в будущем перейти к отмеченным элементам (хотя пустая трата памяти)
Создание массива уникальных массивов с использованием поля [2] в качестве идентификатора:
[ [ '497', 'Q0', 'WTX091-B06-138', '0', '1.000000000', 'GROUP001' ],
[ '497', 'Q0', 'WTX091-B09-92', '1', '0.866899288', 'GROUP001' ],
[ '497', 'Q0', 'WTX091-B09-92', '2', '0.846036819', 'GROUP001' ],
[ '497', 'Q0', 'WTX091-B09-57', '3', '0.835025326', 'GROUP001' ],
[ '497', 'Q0', 'WTX091-B43-79', '4', '0.765068215', 'GROUP001' ],
[ '497', 'Q0', 'WTX091-B43-56', '5', '0.764211464', 'GROUP001' ],
[ '497', 'Q0', 'WTX091-B44-448', '6', '0.761701704', 'GROUP001' ],
[ '497', 'Q0', 'WTX091-B44-12', '7', '0.761701704', 'GROUP001' ],
[ '497', 'Q0', 'WTX091-B49-128', '8', '0.747434800', 'GROUP001' ],
[ '497', 'Q0', 'WTX091-B18-17', '9', '0.746724770', 'GROUP001' ],
[ '497', 'Q0', 'WTX091-B19-374', '10', '0.733379549', 'GROUP001' ],
[ '497', 'Q0', 'WTX091-B19-344', '11', '0.731421782', 'GROUP001' ],
[ '497', 'Q0', 'WTX091-B09-92', '12', '0.726450470', 'GROUP001' ],
[ '497', 'Q0', 'WTX091-B19-174', '13', '0.712757036', 'GROUP001' ] ]
.filter((val1, idx1, arr) => !!~val1.indexOf(val1[2]) &&
!(arr.filter((val2, idx2) => !!~val2.indexOf(val1[2]) &&
idx2 < idx1).length));
Уже есть куча отличных ответов. Вот мой подход.
var removeDuplicates = function(nums) {
let filteredArr = [];
nums.forEach((item) => {
if(!filteredArr.includes(item)) {
filteredArr.push(item);
}
})
return filteredArr;
}
Старый способ с двумя петлями.
let a = ["Messi", 23, false, 11, 17, true, 23, "Messi", "Sachin", "2023", true, "Sachin"];
let b = [];
for (let i in a) {
let found = false;
for (let j in b) {
if (a[i] === b[j]) {
found = true;
break;
}
}
if (found === false) {
b.push(a[i]);
}
}
console.log(b); //['Messi', 23, false, 11, 17, true, 'Sachin', '2023']
ES2016 .includes() Один метод Простой ответ:
var arr = [1,5,2,4,1,6]
function getOrigs(arr) {
let unique = []
arr && arr.forEach(number => {
!unique.includes(number) && unique.push(number)
if (number === arr[arr.length - 1]) {
console.log('unique: ', unique)
}
})
}
getOrigs(arr)
Используйте это вместо этого:
- более поздняя версия ES
- Простой вопрос не должен использовать несколько расширенных методов JS, а push(), length() и forEach() являются общими.
- Более легкая читаемость с использованием замыкания
- Кажется, лучше по памяти, сборке мусора и производительности, чем другие
- Меньше строк кода: если вы разделите строки в зависимости от того, где заканчивается строка, вам понадобится только одна строка логики (так что вы можете вызывать или рефакторить этот однострочный код, как хотите):
var arr = [1,5,2,4,1,6];
function getOrigs(arr) {let unique = [];
arr && arr.forEach(number => !unique.includes(number) && unique.push(number) && ((number === arr[arr.length - 1]) && console.log('unique: ', unique)))};
getOrigs(arr);
Вы можете просто использовать встроенную функцию
Array.prototype.filter()
array.filter((x, y) => array.indexOf(x) == y);
Я хотел удалить дубликаты из массива объектов. Дубликаты имели одинаковые идентификаторы. Вот что я сделал.
// prev data
const prev = [
{
id: 1,
name: "foo",
},
{
id: 2,
name: "baz",
},
{
id: 1,
name: "foo",
},
];
// method:
// Step 1: put them in an object with the id as the key. Value of same id would get overriden.
// Step 2: get all the values.
const tempObj = {};
prev.forEach((n) => (tempObj[n.id] = n));
const next = Object.values(tempObj);
// result
[
{
id: 1,
name: "foo",
},
{
id: 2,
name: "baz",
}
];
Попробуйте сделать это:
Это перебирает массив, проверяет, является ли первый найденный результат той же записи в массиве текущим индексом, и если да, то позволяет ему быть в массиве.
Иногда мне нужно получить уникальные вхождения из массива объектов. Lodash кажется хорошим помощником, но я не думаю, что фильтрация массива оправдывает добавление зависимости в проект.
Давайте предположим, что сравнение двух объектов представляет собой сравнение свойства, например, идентификатора.
const a = [{id: 3}, {id: 4}, {id: 3}, {id: 5}, {id: 5}, {id: 5}];
Так как мы все любим отрывки из одной строки, вот как это можно сделать:
a.reduce((acc, curr) => acc.find(e => e.id === curr.id) ? acc : [...acc, curr], [])
var elems = ['f', 'a','b','f', 'c','d','e','f','c', 'n', 'n'];
elems.sort();
elems.forEach(function (value, index, arr){
let first_index = arr.indexOf(value);
let last_index = arr.lastIndexOf(value);
if(first_index === last_index){
console.log('unique items in array ' + value);
}else{
console.log('Duplicate item in array ' + value);
}
});
Однострочный код даст уникальные значения. Распространение оператора с новым Set.
let nos = [1, 2, 3, 4, 5, 6, 2, 4, 5, 6];
let uniqueNos = [...new Set(nos)];
console.log(uniqueNos); //will print 1,2,3,4,5,6
Это решение должно быть очень быстрым и будет работать во многих случаях.
- Преобразовать элементы индексированного массива в ключи объекта
Используйте функцию Object.keys
var indexArray = ["hi","welcome","welcome",1,-9]; var keyArray = {}; indexArray.forEach(function(item){ keyArray[item]=null; }); var uniqueArray = Object.keys(keyArray);
Ответ Object выше, кажется, не работает для меня в моем случае использования с Objects.
Я изменил это следующим образом:
var j = {};
this.forEach( function(v) {
var typ = typeof v;
var v = (typ === 'object') ? JSON.stringify(v) : v;
j[v + '::' + typ] = v;
});
return Object.keys(j).map(function(v){
if ( v.indexOf('::object') > -1 ) {
return JSON.parse(j[v]);
}
return j[v];
});
Похоже, теперь это работает правильно для объектов, массивов, массивов со смешанными значениями, логических значений и т. Д.
У меня есть простой пример, где мы можем удалить объекты из массива с повторным идентификатором в объектах,
let data = new Array({id: 1},{id: 2},{id: 3},{id: 1},{id: 3});
let unique = [];
let tempArr = [];
console.log('before', data);
data.forEach((value, index) => {
if (unique.indexOf(value.id) === -1) {
unique.push(value.id);
} else {
tempArr.push(index);
}
});
tempArr.reverse();
tempArr.forEach(ele => {
data.splice(ele, 1);
});
console.log(data);