Получить все неуникальные значения (т.е. дубликаты / более одного вхождения) в массиве

Мне нужно проверить массив JavaScript, чтобы увидеть, есть ли какие-либо дубликаты значений. Какой самый простой способ сделать это? Мне просто нужно найти дублирующиеся значения - мне не нужны их индексы или сколько раз они дублируются.

Я знаю, что могу пройтись по массиву и проверить все другие значения на совпадение, но, похоже, должен быть более простой способ. Есть идеи? Спасибо!

Подобный вопрос:

99 ответов

Решение

Вы можете отсортировать массив, а затем выполнить его и посмотреть, совпадает ли следующий (или предыдущий) индекс с текущим. Предполагая, что ваш алгоритм сортировки хорош, это должно быть меньше, чем O (n 2):

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-loop или 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' ]

Более функциональный синтаксис:

@ Дмитрий-Лаптин указал, что нужно удалить код. Это более компактная версия того же кода. Используя некоторые трюки ES6 и функции высшего порядка:

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' ]

Используя Function.prototype.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);

Когда все, что вам нужно, это проверить, что нет дубликатов, как задано в этом вопросе, вы можете использовать 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

Найти повторяющиеся значения в массиве

Это должен быть один из самых коротких способов найти дублирующиеся значения в массиве. Как специально запрашивается OP, это не удаляет дубликаты, а находит их.

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() (или, чтобы быть более понятным: оператор строгого сравнения).

Из-за lower () и indexOf() ему нужен как минимум IE 9.

Вы можете добавить эту функцию или настроить ее и добавить в прототип Javascript Array:

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;
    }
  });
}
var a = ["a","a","b","c","c"];

a.filter(function(value,index,self){ return (self.indexOf(value) !== index )})

Это должно дать вам то, что вы хотите, просто дубликаты.

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.

Найти уникальные значения из 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]

Просто полифил для массива 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);

вместо добавления 'Array.prototype.indexOf'

Самый простой и быстрый способ - использовать Set объект:

      const numbers = [1, 2, 3, 2, 4, 5, 5, 6];

const set = new Set(numbers);

const duplicates = numbers.filter(item => {
    if (set.has(item)) {
        set.delete(item);
    } else {
        return item;
    }
});

console.log(duplicates);
// [ 2, 5 ]

Используя underscore.js

function hasDuplicate(arr){
    return (arr.length != _.uniq(arr).length);
}

Это мое предложение (ES6):

let a = [1, 2, 3, 4, 2, 2, 4, 1, 5, 6]
let b = [...new Set(a.sort().filter((o, i) => o === a[i + 1]))]

// b is now [1, 2, 4]

Вот самое простое решение, которое я мог придумать:

    const arr = [-1, 2, 2, 2, 0, 0, 0, 500, -1, 'a', 'a', 'a']

    const filtered = arr.filter((el, index) => arr.indexOf(el) !== index)
    // => filtered = [ 2, 2, 0, 0, -1, 'a', 'a' ]

    const duplicates = [...new Set(filtered)]

    console.log(duplicates)
    // => [ 2, 0, -1, 'a' ]

Вот и все.

Примечание:

  1. Работает с любыми числами, включая 0, строки и отрицательные числа, например -1- Связанный вопрос: получить все уникальные значения в массиве JavaScript (удалить дубликаты)

  2. Исходный массив arr сохраняется (filter возвращает новый массив вместо изменения оригинала)

  3. В filteredмассив содержит все дубликаты; он также может содержать более одного значения (например, наш фильтрованный массив здесь[ 2, 2, 0, 0, -1, 'a', 'a' ])

  4. Если вы хотите получить только повторяющиеся значения (вы не хотите иметь несколько дубликатов с одинаковым значением), вы можете использовать[...new Set(filtered)](ES6 имеет набор объектов, который может хранить только уникальные значения)

Надеюсь это поможет.

Вот мое простое и однострочное решение.

Сначала он ищет не уникальные элементы, а затем делает найденный массив уникальным с помощью 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))]
);

Один лайнер

var arr = [9,1,2,4,3,4,9]
console.log(arr.filter((ele,indx)=>indx!==arr.indexOf(ele))) //get the duplicates
console.log(arr.filter((ele,indx)=>indx===arr.indexOf(ele))) //remove the duplicates

Кратчайший ванильный JS:

[1,1,2,2,2,3].filter((v,i,a) => a.indexOf(v) !== i) // [1, 2, 2]

Вы можете использовать метод фильтра и indexOf(), чтобы получить все повторяющиеся значения

      function duplicate(arr) {
    return duplicateArray = arr.filter((item, index) => arr.indexOf(item) !== index) 
}

arr.indexOf (item) всегда будет возвращать первый индекс, по которому можно найти данный элемент

Быстрый и элегантный способ с использованием деструктурирования и уменьшения объектов 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']

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;});

или при добавлении в prototyp.chain массива

//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

С ES6 (или с помощью Babel или Typescipt) вы можете просто сделать:

var duplicates = myArray.filter(i => myArray.filter(ii => ii === i).length > 1);

https://es6console.com/j58euhbt/

Вот очень легкий и простой способ:

var codes = dc_1.split(',');
var i = codes.length;
while (i--) {
  if (codes.indexOf(codes[i]) != i) {
    codes.splice(i,1);
  }
}

Простой код с синтаксисом 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]);

Ответы с более высоким рейтингом имеют несколько внутренних проблем, включая использование устаревшего javascript, неправильный порядок или поддержку только двух повторяющихся элементов.

Вот современное решение, которое устраняет эти проблемы:

const arrayNonUniq = array => {
    if (!Array.isArray(array)) {
        throw new TypeError("An array must be provided!")
    }

    return array.filter((value, index) => array.indexOf(value) === index && array.lastIndexOf(value) !== index)
}

arrayNonUniq([1, 1, 2, 3, 3])
//=> [1, 3]

arrayNonUniq(["foo", "foo", "bar", "foo"])
//=> ['foo']

Вы также можете использовать пакет npm array-non-uniq.

ES6 предлагает структуру данных Set, которая в основном представляет собой массив, который не принимает дубликаты. С помощью структуры данных 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;
}

Этот ответ также может быть полезен, он использует js reduce оператор / метод удаления дубликатов из массива.

const result = [1, 2, 2, 3, 3, 3, 3].reduce((x, y) => x.includes(y) ? x : [...x, y], []);

console.log(result);

Я только что выяснил простой способ добиться этого с помощью фильтра массива

    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);

Похоже, что следующая функция (уже упомянутая разновидность функции removeDuplicates) делает свое дело, возвращая 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;
}

Только ES5 (т. Е. Ему необходим polyfill для filter () для 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] );
    });

Следующая логика будет проще и быстрее

// @Param:data:Array that is the source 
// @Return : Array that have the duplicate entries
findDuplicates(data: Array<any>): Array<any> {
        return Array.from(new Set(data)).filter((value) => data.indexOf(value) !== data.lastIndexOf(value));
      }

Преимущества:

  1. Одна строка:-P
  2. Вся встроенная структура данных, помогающая повысить эффективность
  3. Быстрее

Описание логики:

  1. Преобразование в набор для удаления всех дубликатов
  2. Итерация по заданным значениям
  3. С каждой проверкой заданного значения в исходном массиве для условия "значения первого индекса не равны последнему индексу" == > Затем выводится как дубликат, иначе он "уникален"

Примечание: методы map() и filter() эффективны и быстрее.

Другие вопросы по тегам