¿De dónde vienen los duplicados y por qué son un problema?
\nImagina: estás recopilando datos de una API, analizando un archivo CSV o procesando la entrada del usuario. En el 90% de los casos, te encuentras con arrays en los que el mismo elemento aparece varias veces. Esto no es solo un problema estético: los duplicados pueden distorsionar las estadísticas, romper el renderizado de listas o provocar bucles infinitos en los algoritmos.
\nLa pregunta "Remove duplicate values from a JavaScript array" en Stack Overflow ha acumulado miles de visitas por una buena razón. Es una operación básica pero críticamente importante que todo desarrollador debe conocer. Hoy miraremos bajo el capó y analizaremos cómo funcionan diferentes enfoques, desde los más ingenuos hasta los más eficientes.
\n\nMétodo n.º 1: Set — el más elegante y rápido
\nSi le preguntas a un desarrollador JavaScript moderno: "¿Cómo eliminar duplicados?", lo primero que dirá es Set. Y es correcto. Pero, ¿cómo funciona?
const numbers = [1, 2, 2, 3, 4, 4, 5];\nconst uniqueNumbers = [...new Set(numbers)];\nconsole.log(uniqueNumbers); // [1, 2, 3, 4, 5]\n\n¿Qué sucede bajo el capó de Set?
\nEl objeto Set almacena valores únicos de cualquier tipo. Cuando pasas un array al constructor new Set(numbers), el motor de JavaScript (V8, SpiderMonkey, etc.) realiza los siguientes pasos:
- \n
- Iteración sobre el array: el motor recorre cada elemento del array original. \n
- Verificación de unicidad: para cada elemento se llama a una función hash interna. Si el elemento ya está en el "cubo" (bucket), se ignora. Si no, se añade. \n
- Ensamblaje del resultado: el operador
[...set](spread) expande el objeto iterable Set de vuelta a un array. \n
Un detalle importante: Set utiliza el algoritmo de comparación SameValueZero. Esto significa que NaN se considera igual a NaN (a diferencia de ===), mientras que +0 y -0 se consideran valores diferentes. En la práctica, esto rara vez importa, pero es bueno saberlo.
Rendimiento de Set
\nLa complejidad de la operación es O(n). Cada elemento se verifica exactamente una vez. Este es el mejor indicador entre todos los métodos. Para arrays de 10 000 elementos, la diferencia entre Set y otros métodos puede ser de decenas de milisegundos, algo crítico para aplicaciones de alta carga.
\n\nMétodo n.º 2: filter + indexOf — el clásico del género
\nAntes de la llegada de ES6 (2015), los desarrolladores usaban la combinación de filter e indexOf. Este método todavía se encuentra en código heredado:
const numbers = [1, 2, 2, 3, 4, 4, 5];\nconst uniqueNumbers = numbers.filter((value, index, self) => {\n return self.indexOf(value) === index;\n});\nconsole.log(uniqueNumbers); // [1, 2, 3, 4, 5]\n\n¿Cómo funciona?
\n- \n
filterrecorre cada elemento del array. \n - Para cada elemento se llama a
indexOf(value), que devuelve el primer índice donde aparece ese valor. \n - Si el índice actual del elemento coincide con el primero encontrado, significa que es la primera aparición y el elemento se conserva. Si no coincide, es un duplicado y se descarta. \n
Inconvenientes
\nEl principal problema es el rendimiento. indexOf internamente utiliza una búsqueda lineal (O(n) por cada llamada). Como resultado, para un array de N elementos, la complejidad se convierte en O(n²). Con 1000 elementos aún es tolerable, pero con 100 000, la ralentización se vuelve notable.
Otra desventaja: indexOf no funciona correctamente con NaN. Si el array contiene NaN, indexOf(NaN) siempre devolverá -1, y el elemento se añadirá repetidamente. Para verificar NaN se necesita usar