Búsqueda de sitios web

javascript - Obtener todos los valores no únicos (es decir: duplicados/más de una aparición) en un array


Necesito comprobar un arreglo de JavaScript para ver si hay valores duplicados. ¿Cuál es la manera más fácil de hacer esto? Solo necesito encontrar cuáles son los valores duplicados, no necesito sus índices ni cuántas veces se repiten.

Sé que puedo recorrer el arreglo y revisar todos los demás valores para encontrar una coincidencia, pero parece que debería haber una manera más fácil.

Pregunta similar:

  • Obtener todos los valores únicos en un arreglo de JavaScript (eliminar duplicados)

Respuesta #1

Podrías ordenar el arreglo y luego recorrerlo y luego ver si el siguiente (o anterior) índice es el mismo que el actual. Suponiendo que tu algoritmo de ordenamiento sea bueno, esto debería ser menor que O(n2):

const findDuplicates = (arr) => {
  let 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)
  let results = [];
  for (let i = 0; i < sorted_arr.length - 1; i++) {
    if (sorted_arr[i + 1] == sorted_arr[i]) {
      results.push(sorted_arr[i]);
    }
  }
  return results;
}

let duplicatedArray = [9, 9, 111, 2, 3, 4, 4, 5, 7];
console.log(`The duplicates in ${duplicatedArray} are ${findDuplicates(duplicatedArray)}`);

En caso de que debas devolverlo como una función para duplicados. Esto es para un tipo de caso similar.

Referencia: https://stackoverflow.com/a/57532964/8119511

Respuesta #2

Si quieres eliminar los duplicados, prueba esta gran solución:

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

console.log(eliminateDuplicates([1,6,7,3,6,8,1,3,4,5,1,7,2,6]))

Fuente: http://dreaminginjavascript.wordpress.com/2008/08/22/eliminating-duplicates/

Respuesta #3

Esta es mi respuesta del hilo duplicado (!):

Al escribir esta entrada en 2014, todos los ejemplos eran bucles for o jQuery. JavaScript tiene las herramientas perfectas para esto: sort, map y reduce.

Encontrar elementos duplicados

var names = ['Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl']

const uniq = names
  .map((name) => {
    return {
      count: 1,
      name: name
    };
  })
  .reduce((result, b) => {
    result[b.name] = (result[b.name] || 0) + b.count;

    return result;
  }, {});
const duplicates = Object.keys(uniq).filter((a) => uniq[a] > 1);

console.log(duplicates); // [ 'Nancy' ]

Sintaxis más funcional:

@Dmytro-Laptin señaló algún código que se puede eliminar. Esta es una versión más compacta del mismo código. Usando algunos trucos de ES6 y funciones de orden superior:

const names = ['Mike', 'Matt', 'Nancy', 'Adam', 'Jenny', 'Nancy', 'Carl'];
const count = names =>
  names.reduce((result, value) => ({ ...result,
    [value]: (result[value] || 0) + 1
  }), {}); // don't forget to initialize the accumulator
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' ]

Respuesta #4

ACTUALIZADO: Corto comando de una línea para obtener los duplicados:

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

Para obtener el arreglo sin duplicados, simplemente invierte la condición:

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

Ten en cuenta que el objetivo principal de esta respuesta es ser breve. Si necesitas algo eficiente para un gran arreglo, una posible solución es ordenar tu arreglo primero (si es ordenable) y luego hacer lo siguiente para obtener el mismo tipo de resultados que los anteriores:

myHugeSortedArray.filter((e, i, a) => a[i-1] === e)

Aquí hay un ejemplo para un arreglo de 1 000 000 de enteros:

const myHugeIntArrayWithDuplicates =
  [...Array(1_000_000).keys()]
  // adding two 0 and four 9 duplicates
  .fill(0, 2, 4).fill(9, 10, 14)

console.time("time")
console.log(
  myHugeIntArrayWithDuplicates
  // a possible sorting method for integers
  .sort((a, b) => a > b ? 1 : -1)
  .filter((e, i, a) => a[i-1] === e)
)
console.timeEnd("time")

En mi máquina de desarrollo con AMD Ryzen 7 5700G muestra:

[ 0, 0, 9, 9, 9, 9 ]
time: 22.738ms

Como se señaló en los comentarios, tanto la solución corta como la solución eficiente devolverán un arreglo con varias veces el mismo duplicado si ocurre más de una vez en el arreglo original:

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

Si se desean duplicados únicos, entonces una función como

function duplicates(arr) {
  return [...new Set(arr.filter((e, i, a) => a.indexOf(e) !== i))]
}

puede usarse para que duplicates([1, 1, 1, 2, 2, 2, 2]) devuelva [1, 2].


Cuando solo necesitas comprobar que no hay duplicados como se pide en esta pregunta, puedes usar el método every():

[1, 2, 3].every((e, i, a) => a.indexOf(e) === i) // true

[1, 2, 1].every((e, i, a) => a.indexOf(e) === i) // false

Tenga en cuenta que every() no funciona en IE 8 y versiones anteriores.

Respuesta #5

Encontrar valores duplicados en un arreglo

Esta debería ser una de las formas más cortas de encontrar realmente valores duplicados en un arreglo. Tal como lo solicitó específicamente el OP, esto no elimina los duplicados, sino que los encuentra.

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

// Or, using Sets (about 4 times faster)

var duplicates = Array.from(items.reduce((acc, v, i, arr) {
  return arr.indexOf(v) !== i ? acc.add(v) : acc;
}, new Set()))

Esto no necesita ordenamiento ni ningún framework de terceros. Tampoco necesita bucles manuales. Funciona con cualquier valor que indexOf() (o para ser más claro: el operador de comparación estricta) soporte.

Debido a reduce() e indexOf(), requiere al menos IE 9.

Respuesta #6

Puedes agregar esta función, o modificarla y agregarla al prototipo de Array de 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);

Respuesta #7

ACTUALIZADO: Lo siguiente utiliza una estrategia combinada optimizada. Optimiza las búsquedas de primitivos para aprovechar el tiempo de búsqueda hash O(1) (ejecutar unique en un array de primitivos es O(n)). Las búsquedas de objetos se optimizan etiquetando los objetos con un id único mientras se itera, de modo que identificar objetos duplicados también es O(1) por elemento y O(n) para toda la lista. La única excepción son los elementos que están congelados, pero estos son raros y se proporciona una alternativa utilizando un array y 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;
}();

Si tienes disponibles las Colecciones ES6, entonces hay una versión mucho más simple y significativamente más rápida. (shim para IE9+ y otros navegadores aquí: 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;
    }
  });
}

Respuesta #8

var a = ["a","a","b","c","c"];

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

Respuesta #9

Esto debería darte lo que quieres, solo los duplicados.

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.

Respuesta #10

Encuentra valores no únicos de 3 matrices (o más):

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,0],
    merged,
    nonUnique;

// Combine all the arrays to a single one
merged = arr.concat(arr2, arr3)

// create a new (dirty) Array with only the non-unique items
nonUnique = merged.filter((item,i) => merged.includes(item, i+1))

// Cleanup - remove duplicate & empty items items 
nonUnique = [...new Set(nonUnique)]

console.log(nonUnique)

ANTES DE ES2015:

En el ejemplo a continuación elegí superponer un método unique sobre el Array prototype, permitiendo el acceso desde cualquier lugar y tiene una sintaxis más "declarativa". No recomiendo este enfoque en proyectos grandes, ya que muy bien podría colisionar con otro método con el mismo nombre personalizado.

Array.prototype.unique = function () {
    var arr = this.sort(), i=arr.length; // input must be sorted for this to work
    while(i--)
      arr[i] === arr[i-1] && arr.splice(i,1) // remove duplicate item
    return arr
}

Array.prototype.nonunique = function () {
    var arr = this.sort(), i=arr.length, res = []; // input must be sorted for this to work
    while(i--)
      arr[i] === arr[i-1] && (res.indexOf(arr[i]) == -1) && res.push(arr[i]) 
    return res
}

//          🚩🚩  🚩    🚩            🚩 
var arr =  [1,2,2,3,3,4,5,6,2,3,7,8,5,22],
    arr2 = [1,2,511,12,50],
    arr3 = [22,0],
    // merge all arrays & call custom Array Prototype - "unique"
    unique = arr.concat(arr2, arr3).unique(),
    nonunique = arr.concat(arr2, arr3).nonunique()

console.log(unique)     // [1,12,2,22,3,4,5,50,511,6,7,8]
console.log(nonunique)  // [1,12,2,22,3,4,5,50,511,6,7,8]

Respuesta #11

La forma más simple y rápida es usar el objeto 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);
        return false;
    } else {
        return true;
    }
});

// OR more concisely

const duplicates = numbers.filter(item => !set.delete(item));

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

Respuesta #12

usando underscore.js

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

Artículos relacionados