De onde vêm as duplicatas e por que isso é um problema?
\nImagine: você está coletando dados de uma API, analisando um arquivo CSV ou processando a entrada do usuário. Em 90% dos casos, você encontrará arrays onde o mesmo elemento aparece várias vezes. Isso não é apenas um problema estético — duplicatas podem distorcer estatísticas, quebrar a renderização de listas ou levar a loops infinitos em algoritmos.
\nA pergunta "Remove duplicate values from a JavaScript array" no Stack Overflow acumulou milhares de visualizações por um bom motivo. Esta é uma operação básica, mas criticamente importante, que todo desenvolvedor deve conhecer. Hoje vamos analisar detalhadamente como diferentes abordagens funcionam — desde as mais ingênuas até as mais eficientes.
\n\nMétodo nº1: Set — o mais elegante e rápido
\nSe você perguntar a um desenvolvedor JavaScript moderno: "Como remover duplicatas?", a primeira coisa que ele dirá é Set. E isso está correto. Mas como funciona?
const numbers = [1, 2, 2, 3, 4, 4, 5];\nconst uniqueNumbers = [...new Set(numbers)];\nconsole.log(uniqueNumbers); // [1, 2, 3, 4, 5]\n\nO que acontece internamente no Set?
\nO objeto Set armazena valores únicos de qualquer tipo. Quando você passa um array para o construtor new Set(numbers), o motor JavaScript (V8, SpiderMonkey, etc.) executa os seguintes passos:
- \n
- Iteração pelo array: o motor percorre cada elemento do array original. \n
- Verificação de unicidade: para cada elemento, uma função hash interna é chamada. Se o elemento já existe no "bucket", ele é ignorado. Se não, é adicionado. \n
- Montagem do resultado: o operador
[...set](spread) expande o objeto iterável Set de volta para um array. \n
Uma nuance importante: o Set usa o algoritmo de comparação SameValueZero. Isso significa que NaN é considerado igual a NaN (ao contrário de ===), e +0 e -0 são considerados valores diferentes. Na prática, isso raramente importa, mas é bom saber.
Desempenho do Set
\nA complexidade da operação é O(n). Cada elemento é verificado exatamente uma vez. Este é o melhor desempenho entre todos os métodos. Para arrays com 10.000 elementos, a diferença entre Set e outros métodos pode chegar a dezenas de milissegundos — crítico para aplicações de alta carga.
\n\nMétodo nº2: filter + indexOf — o clássico
\nAntes do ES6 (2015), os desenvolvedores usavam a combinação de filter e indexOf. Este método ainda é encontrado em código legado:
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\nComo funciona?
\n- \n
filterpercorre cada elemento do array. \n - Para cada elemento,
indexOf(value)é chamado, retornando o primeiro índice onde esse valor aparece. \n - Se o índice atual do elemento coincide com o primeiro encontrado — significa que é a primeira ocorrência, e o elemento é mantido. Se não coincide — é uma duplicata e é descartado. \n
Armadilhas
\nO principal problema é o desempenho. indexOf internamente usa busca linear (O(n) para cada chamada). Como resultado, para um array de N elementos, a complexidade se torna O(n²). Com 1000 elementos ainda é tolerável, mas com 100.000, a lentidão se torna perceptível.
Outra desvantagem: indexOf não funciona corretamente com NaN. Se o array contiver NaN, indexOf(NaN) sempre retornará -1, e o elemento será adicionado novamente. Para verificar NaN, é necessário usar