Получить все не уникальные значения (т. е. дублировать/более одного вхождения) в массиве
Мне нужно проверить массив JavaScript, чтобы увидеть, есть ли какие-либо повторяющиеся значения. Как проще всего это сделать? Мне просто нужно найти, что такое дублированные значения - мне на самом деле не нужны их индексы или сколько раз они дублируются.
Я знаю, что могу перебирать массив и проверять все остальные значения на соответствие, но, похоже, должен быть более простой способ. Есть идеи? Спасибо!
аналогичный вопрос:
30 ответов
вы можете отсортировать массив, а затем запустить его, а затем посмотреть, совпадает ли следующий (или предыдущий) индекс с текущим. Предполагая, что ваш алгоритм сортировки хорош, это должно быть меньше O (n2):
var arr = [9, 9, 111, 2, 3, 4, 4, 5, 7];
var sorted_arr = arr.slice().sort(); // You can define the comparing function here.
// JS by default uses a crappy string compare.
// (we use slice to clone the array so the
// original array won't be modified)
var results = [];
for (var i = 0; i < sorted_arr.length - 1; i++) {
if (sorted_arr[i + 1] == sorted_arr[i]) {
results.push(sorted_arr[i]);
}
}
console.log(results);
Если вы хотите устранить дубликаты, попробуйте это отличное решение:
function eliminateDuplicates(arr) {
var i,
len = arr.length,
out = [],
obj = {};
for (i = 0; i < len; i++) {
obj[arr[i]] = 0;
}
for (i in obj) {
out.push(i);
}
return out;
}
источник: http://dreaminginjavascript.wordpress.com/2008/08/22/eliminating-duplicates/
- Это мой ответ с двойной резьбой (!):
устал видеть все плохие примеры с for-loops или jQuery. Javascript имеет идеальные инструменты для этого в настоящее время: сортировка, карта и сокращение.
найти повторяющиеся элементы
var names = ['Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl']
var uniq = names
.map((name) => {
return {count: 1, name: name}
})
.reduce((a, b) => {
a[b.name] = (a[b.name] || 0) + b.count
return a
}, {})
var duplicates = Object.keys(uniq).filter((a) => uniq[a] > 1)
console.log(duplicates) // [ 'Nancy' ]
более функциональный синтаксис:
@Dmytro-Laptin указал, что некоторый код кода будет удален. Это более компактная версия того же кода. Используя некоторые ЕС6 трюки и высшего порядка функции:
const names = ['Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl']
const count = names =>
names.reduce((a, b) =>
Object.assign(a, {[b]: (a[b] || 0) + 1}), {})
const duplicates = dict =>
Object.keys(dict).filter((a) => dict[a] > 1)
console.log(count(names)) // { Mike: 1, Matt: 1, Nancy: 2, Adam: 1, Jenny: 1, Carl: 1 }
console.log(duplicates(count(names))) // [ 'Nancy' ]
С Помощью Функции.прототип.bind:
// prep
const arr = Array.from('Learn more javascript dude');
const counter = (prev, next) => Object.assign(prev, { [next] : (prev[next] || 0) + 1 });
const singles = function(key){ return this[key] === 1 };
const multiples = function(key){ return this[key] > 1 };
// work
const counted = arr.reduce(counter, {});
const filtered = Object.keys(counted).filter(multiples.bind(counted));
//[ "e", "a", "r", " ", "d" ]
console.log(filtered);
найти повторяющиеся значения в массиве
Это должен быть один из самых коротких способов найти повторяющиеся значения в массиве. Как конкретно просил ОП,это не удаляет дубликаты, но находит их.
var input = [1, 2, 3, 1, 3, 1];
var duplicates = input.reduce(function(acc, el, i, arr) {
if (arr.indexOf(el) !== i && acc.indexOf(el) < 0) acc.push(el); return acc;
}, []);
document.write(duplicates); // = 1,3 (actual array == [1, 3])
это не требует сортировки или каких-либо сторонних рамок. Ему также не нужны ручные петли. Он работает с каждым значением indexOf () (или, чтобы быть более ясным:строгое сравнение оператор) поддерживает.
из-за уменьшить() и indexOf () он должен, по крайней мере, т. е. 9.
вы можете добавить эту функцию или настроить ее и добавить в прототип массива Javascript:
Array.prototype.unique = function () {
var r = new Array();
o:for(var i = 0, n = this.length; i < n; i++)
{
for(var x = 0, y = r.length; x < y; x++)
{
if(r[x]==this[i])
{
alert('this is a DUPE!');
continue o;
}
}
r[r.length] = this[i];
}
return r;
}
var arr = [1,2,2,3,3,4,5,6,2,3,7,8,5,9];
var unique = arr.unique();
alert(unique);
обновлено: ниже используется оптимизированная комбинированная стратегия. Он оптимизирует примитивные поиски, чтобы воспользоваться хэшем O (1) время поиска (работает unique
на массиве примитивов есть O (n)). Поиск объектов оптимизируется путем пометки объектов уникальным идентификатором во время итерации, поэтому идентификация повторяющихся объектов также O(1) для каждого элемента и O(n) для всего списка. Единственным исключением являются элементы, которые заморожены, но они редки, и резерв предоставляется с использованием массива и метод indexOf.
var unique = function(){
var hasOwn = {}.hasOwnProperty,
toString = {}.toString,
uids = {};
function uid(){
var key = Math.random().toString(36).slice(2);
return key in uids ? uid() : uids[key] = key;
}
function unique(array){
var strings = {}, numbers = {}, others = {},
tagged = [], failed = [],
count = 0, i = array.length,
item, type;
var id = uid();
while (i--) {
item = array[i];
type = typeof item;
if (item == null || type !== 'object' && type !== 'function') {
// primitive
switch (type) {
case 'string': strings[item] = true; break;
case 'number': numbers[item] = true; break;
default: others[item] = item; break;
}
} else {
// object
if (!hasOwn.call(item, id)) {
try {
item[id] = true;
tagged[count++] = item;
} catch (e){
if (failed.indexOf(item) === -1)
failed[failed.length] = item;
}
}
}
}
// remove the tags
while (count--)
delete tagged[count][id];
tagged = tagged.concat(failed);
count = tagged.length;
// append primitives to results
for (i in strings)
if (hasOwn.call(strings, i))
tagged[count++] = i;
for (i in numbers)
if (hasOwn.call(numbers, i))
tagged[count++] = +i;
for (i in others)
if (hasOwn.call(others, i))
tagged[count++] = others[i];
return tagged;
}
return unique;
}();
Если у вас есть коллекции ES6, то есть гораздо более простая и значительно более быстрая версия. (прокладка для IE9+ и других браузеров здесь:https://github.com/Benvie/ES6-Harmony-Collections-Shim)
function unique(array){
var seen = new Set;
return array.filter(function(item){
if (!seen.has(item)) {
seen.add(item);
return true;
}
});
}
Это должно дать вам то, что вы хотите, только дубликаты.
function find_duplicates(arr) {
var len=arr.length,
out=[],
counts={};
for (var i=0;i<len;i++) {
var item = arr[i];
counts[item] = counts[item] >= 1 ? counts[item] + 1 : 1;
if (counts[item] === 2) {
out.push(item);
}
}
return out;
}
find_duplicates(['one',2,3,4,4,4,5,6,7,7,7,'pig','one']); // -> ['one',4,7] in no particular order.
var a = ["a","a","b","c","c"];
a.filter(function(value,index,self){ return (self.indexOf(value) !== index )})
var a = [324,3,32,5,52,2100,1,20,2,3,3,2,2,2,1,1,1].sort();
a.filter(function(v,i,o){return i&&v!==o[i-1]?v:0;});
или при добавлении в Гамбурге.цепи блока
//copy and paste: without error handling
Array.prototype.unique =
function(){return this.sort().filter(function(v,i,o){return i&&v!==o[i-1]?v:0;});}
смотрите здесь: https://gist.github.com/1305056
когда все, что вам нужно, это проверить, что нет дубликатов, как указано в этот вопрос можно использовать every()
способ:
[1, 2, 3].every(function(elem, i, array){return array.lastIndexOf(elem) === i}) // true
[1, 2, 1].every(function(elem, i, array){return array.lastIndexOf(elem) === i}) // false
отметим, что every()
не работает для IE 8 и ниже.
Я использую lastIndexOf()
потому что это может быть более эффективным, чем indexOf()
если обратные вызовы функции every()
осуществляются в порядке индекса, но это не доказано.
на CoffeeScript я использую этот:
Array::duplicates = -> not @every((elem, i, array) -> array.lastIndexOf(elem) is i)
[1, 2, 3].duplicates() // false
[1, 2, 1].duplicates() // true
найти уникальные значения из 3 массивов (или больше):
Array.prototype.unique = function () {
var arr = this.sort(), i; // input must be sorted for this to work
for( i=arr.length; i--; )
arr[i] === arr[i-1] && arr.splice(i,1); // remove duplicate item
return arr;
}
var arr = [1,2,2,3,3,4,5,6,2,3,7,8,5,9],
arr2 = [1,2,511,12,50],
arr3 = [22],
unique = arr.concat(arr2, arr3).unique();
console.log(unique); // [22, 50, 12, 511, 2, 1, 9, 5, 8, 7, 3, 6, 4]
просто polyfill для массива indexOf для старых браузеров:
if (!Array.prototype.indexOf){
Array.prototype.indexOf = function(elt /*, from*/){
var len = this.length >>> 0;
var from = Number(arguments[1]) || 0;
from = (from < 0) ? Math.ceil(from) : Math.floor(from);
if (from < 0)
from += len;
for (; from < len; from++){
if (from in this && this[from] === elt)
return from;
}
return -1;
};
}
решение jQuery с использованием "inArray":
if( $.inArray(this[i], arr) == -1 )
ES2015
var arr = [1,2,2,3,3,4,5,6,2,3,7,8,5,22],
arr2 = [1,2,511,12,50],
arr3 = [22],
unique;
// Combine all the arrays to a single one
unique = arr.concat(arr2, arr3);
// create a new (dirty) Array with only the unique items
unique = unique.map((item,i) => unique.includes(item, i+1) ? item : '' )
// Cleanup - remove duplicate & empty items items
unique = [...new Set(unique)].filter(n => n);
console.log(unique);
вместо добавления массива.прототип.помощи indexOf'
простой код с синтаксисом ES6 (возвращает отсортированный массив дубликатов):
let duplicates = a => {d=[]; a.sort((a,b) => a-b).reduce((a,b)=>{a==b&&!d.includes(a)&&d.push(a); return b}); return d};
Как использовать:
duplicates([1,2,3,10,10,2,3,3,10]);
следующая функция (вариация уже упомянутой функции eliminateDuplicates), похоже, делает трюк, возвращая test2, 1, 7, 5 для ввода ["test", "test2", "test2", 1, 1, 1, 2, 3, 4, 5, 6, 7, 7, 10, 22, 43, 1, 5, 8]
обратите внимание, что проблема более странная в JavaScript, чем в большинстве других языков, потому что массив JavaScript может содержать почти все. Обратите внимание, что решения, использующие сортировку, могут потребоваться для обеспечения соответствующей функции сортировки-я не пробовал этот маршрут еще не пройден.
эта конкретная реализация работает для (по крайней мере) строк и чисел.
function findDuplicates(arr) {
var i,
len=arr.length,
out=[],
obj={};
for (i=0;i<len;i++) {
if (obj[arr[i]] != null) {
if (!obj[arr[i]]) {
out.push(arr[i]);
obj[arr[i]] = 1;
}
} else {
obj[arr[i]] = 0;
}
}
return out;
}
здесь очень легкий и простой способ:
var codes = dc_1.split(',');
var i = codes.length;
while (i--) {
if (codes.indexOf(codes[i]) != i) {
codes.splice(i,1);
}
}
var arr = [2, 1, 2, 2, 4, 4, 2, 5];
function returnDuplicates(arr) {
return arr.reduce(function(dupes, val, i) {
if (arr.indexOf(val) !== i && dupes.indexOf(val) === -1) {
dupes.push(val);
}
return dupes;
}, []);
}
alert(returnDuplicates(arr));
эта функция избегает шага сортировки и использует метод reduce () для перемещения дубликатов в новый массив, если он еще не существует в нем.
использование "includes" для проверки, если элемент уже существует.
var arr = [1, 1, 4, 5, 5], darr = [], duplicates = [];
for(var i = 0; i < arr.length; i++){
if(darr.includes(arr[i]) && !duplicates.includes(arr[i]))
duplicates.push(arr[i])
else
darr.push(arr[i]);
}
console.log(duplicates);
<h3>Array with duplicates</h3>
<p>[1, 1, 4, 5, 5]</p>
<h3>Array with distinct elements</h3>
<p>[1, 4, 5]</p>
<h3>duplicate values are</h3>
<p>[1, 5]</p>
ES6 предлагает структуру данных Set, которая в основном является массивом, который не принимает дубликаты. С заданной структурой данных, есть очень простой способ найти дубликаты в массиве (используя только один цикл).
вот мой код
function findDuplicate(arr) {
var set = new Set();
var duplicates = new Set();
for (let i = 0; i< arr.length; i++) {
var size = set.size;
set.add(arr[i]);
if (set.size === size) {
duplicates.add(arr[i]);
}
}
return duplicates;
}
С ES6 (или с помощью Babel или Typescipt) вы можете просто сделать:
var duplicates = myArray.filter(i => myArray.filter(ii => ii === i).length > 1);
Я только что выяснил простой способ достичь этого с помощью фильтра массива
var list = [9, 9, 111, 2, 3, 4, 4, 5, 7];
// Filter 1: to find all duplicates elements
var duplicates = list.filter(function(value,index,self) {
return self.indexOf(value) !== self.lastIndexOf(value) && self.indexOf(value) === index;
});
console.log(duplicates);
вот мое простое и одно линейное решение.
сначала он ищет не уникальные элементы, а затем делает найденный массив уникальным с использованием Set.
таким образом, у нас есть массив дубликатов в конце.
var array = [1, 2, 2, 3, 3, 4, 5, 6, 2, 3, 7, 8, 5, 22, 1, 2, 511, 12, 50, 22];
console.log([...new Set(
array.filter((value, index, self) => self.indexOf(value) !== index))]
);
быстрый и элегантный способ с помощью es6 объект деструктурирования и уменьшить
он работает в O (n) (1 итерация по массиву) и не повторяет значения, которые появляются более 2 раз
const arr = ['hi', 'hi', 'hi', 'bye', 'bye', 'asd']
const {
dup
} = arr.reduce(
(acc, curr) => {
acc.items[curr] = acc.items[curr] ? acc.items[curr] += 1 : 1
if (acc.items[curr] === 2) acc.dup.push(curr)
return acc
}, {
items: {},
dup: []
},
)
console.log(dup)
// ['hi', 'bye']
просто чтобы добавить некоторую теорию к вышесказанному.
поиск дубликатов имеет нижнюю границу O(n*log (n) в модели сравнения. Поэтому теоретически вы не можете сделать лучше, чем первая сортировка, а затем пройти через список последовательно удаление любых дубликатов, которые вы найдете.
Если вы хотите найти дубликаты в linear(O (n)) ожидается время, вы могли бы хэш каждый элемент списка; если есть столкновение, удалите / пометьте его как дубликат, и продолжать.
только ES5 (т. е. ему нужен фильтр () polyfill для IE8 и ниже):
var arrayToFilter = [ 4, 5, 5, 5, 2, 1, 3, 1, 1, 2, 1, 3 ];
arrayToFilter.
sort().
filter( function(me,i,arr){
return (i===0) || ( me !== arr[i-1] );
});
var input = ['a', 'b', 'a', 'c', 'c'],
duplicates = [],
i, j;
for (i = 0, j = input.length; i < j; i++) {
if (duplicates.indexOf(input[i]) === -1 && input.indexOf(input[i], i+1) !== -1) {
duplicates.push(input[i]);
}
}
console.log(duplicates);
Я думаю, что ниже самый простой и быстрый способ O(n) выполнить именно то, что вы просили:
function getDuplicates( arr ) {
var i, value;
var all = {};
var duplicates = [];
for( i=0; i<arr.length; i++ ) {
value = arr[i];
if( all[value] ) {
duplicates.push( value );
all[value] = false;
} else if( typeof all[value] == "undefined" ) {
all[value] = true;
}
}
return duplicates;
}
или для ES5 или выше:
function getDuplicates( arr ) {
var all = {};
return arr.reduce(function( duplicates, value ) {
if( all[value] ) {
duplicates.push(value);
all[value] = false;
} else if( typeof all[value] == "undefined" ) {
all[value] = true;
}
return duplicates;
}, []);
}
изменение решения @RaphaelMontanaro, заимствуя из блога @Nosredna, вот что вы можете сделать, если вы просто хотите идентифицировать повторяющиеся элементы из своего массива.
function identifyDuplicatesFromArray(arr) {
var i;
var len = arr.length;
var obj = {};
var duplicates = [];
for (i = 0; i < len; i++) {
if (!obj[arr[i]]) {
obj[arr[i]] = {};
}
else
{
duplicates.push(arr[i]);
}
}
return duplicates;
}
Спасибо за элегантное решение, @Nosredna!
мне не понравилось большинство ответов.
почему? Слишком сложный, слишком много кода, неэффективный код и многие не отвечают на вопрос, который состоит в том, чтобы найти дубликаты (и не давать массив без дубликатов).
следующая функция возвращает все дубликаты:
function GetDuplicates(arr) {
var i, out=[], obj={};
for (i=0; i < arr.length; i++)
obj[arr[i]] == undefined ? obj[arr[i]] ++ : out.push(arr[i]);
return out;
}
потому что большую часть времени бесполезно возвращать все дубликаты, а просто сказать, какие дубликаты существуют. В этом случае вы возвращаете массив с уникальными дубликатами ;-)
function GetDuplicates(arr) {
var i, out=[], obj={};
for (i=0; i < arr.length; i++)
obj[arr[i]] == undefined ? obj[arr[i]] ++ : out.push(arr[i]);
return GetUnique(out);
}
function GetUnique(arr) {
return $.grep(arr, function(elem, index) {
return index == $.inArray(elem, arr);
});
}
может, кто-то еще думает так же.
Я предпочитаю функциональный способ сделать это.
function removeDuplicates(links) {
return _.reduce(links, function(list, elem) {
if (list.indexOf(elem) == -1) {
list.push(elem);
}
return list;
}, []);
}
Это использует подчеркивание, но массив имеет reduce
решить за o(n) сложность времени (без сортировки).
var arr = [9, 9, 111, 2, 3, 4, 4, 5, 7];
var obj={};
for(var i=0;i<arr.length;i++){
if(!obj[arr[i]]){
obj[arr[i]]=1;
} else {
obj[arr[i]]=obj[arr[i]]+1;
}
}
var result=[]
for(var key in obj){
if(obj[key]>1){
result.push(Number(key)) // change this to result.push(key) to find duplicate strings in an array
}
}
console.log(result)