Demo Tipo · 01

Compare algumas formas de resolver o mesmo problema

Use isto quando houver 3–4 soluções razoáveis e a lição for o trade-off — cartões lado a lado com o código, o que é bom, o que não é, e um simples "escolha isto quando" para cada um.

Este é um exemplar copiável. Para usá-lo em um curso, transponha o bloco <section class="demo"> abaixo para uma lição construída a partir de assets/lesson-template.html — os design tokens e o CSS dos componentes já combinam.

Leia a versão simples, ou abra a camada técnica em qualquer seção.
1

A ideia central


Digamos que você tenha uma lista com repetições — [3, 1, 3, 2, 1] — e queira uma cópia limpa com cada valor mantido apenas uma vez: [3, 1, 2]. Em JavaScript há pelo menos três formas honestas de fazer isso. Nenhuma delas é "a resposta certa". Cada uma faz uma barganha diferente entre o quão curta ela é, o quão rápido ela roda, e o quanto ela consegue lidar.

O trabalho desta lição não é eleger um vencedor. É colocar as três lado a lado para que você veja a barganha que cada uma faz, e escolha de propósito.

Pense nisso como… três rotas de volta para casa em um mapa. A rodovia é a mais rápida, mas você precisa entrar nela. O atalho pela cidade é fácil de lembrar, mas lento na hora do rush. A estrada cênica deixa você parar onde quiser, mas leva mais tempo. Mesmo destino, motivos diferentes para escolher cada uma.

O que "desduplicar" significa aqui

Mantemos a primeira ocorrência de cada valor e preservamos a ordem. A igualdade é o SameValueZero do JavaScript — a mesma regra que um Set usa — então NaN é desduplicado corretamente e +0/-0 se fundem. Todas as três abordagens abaixo produzem [3, 1, 2] para a entrada [3, 1, 3, 2, 1]; elas diferem no custo e em como se comportam à medida que a lista cresce.

2

Três formas, lado a lado


Cada cartão mostra o código de verdade, no que ele é bom, onde ele morde, e uma frase única sobre quando recorrer a ele. Passe o mouse ou foque um cartão para trazê-lo à frente.

A

new Set()

Despeje a lista em um Set — que não pode guardar duplicatas — e depois espalhe-o de volta para um array.

const unique = function (list) {
  return [...new Set(list)];
};

unique([3, 1, 3, 2, 1]);
// → [3, 1, 2]

Prós

  • +A mais curta de ler e escrever — uma linha.
  • +Rápida: aproximadamente linear, escala para listas grandes.
  • +Mantém a ordem original; lida com NaN.

Contras

  • Desduplica apenas por identidade — sem "igual" customizado.
  • Objetos comparados por referência, não pelo conteúdo.
Escolha isto quando Os valores são primitivos (números, strings) e você quer a coisa mais simples e rápida. Este é o padrão.
B

filter() + indexOf

Mantenha um item apenas se este for o primeiro lugar em que ele aparece — ou seja, seu índice é igual ao seu primeiro índice.

const unique = function (list) {
  return list.filter(function (v, i) {
    return list.indexOf(v) === i;
  });
};

unique([3, 1, 3, 2, 1]);
// → [3, 1, 2]

Prós

  • +Sem Set — funciona em runtimes muito antigos.
  • +Lê-se como sua definição: "primeira vez vista".

Contras

  • Lenta: varre a lista dentro da lista — quadrática.
  • indexOf não consegue achar NaN, então ele permanece duplicado.
Escolha isto quando A lista é pequena e você quer uma linha autoexplicativa sem estrutura de dados extra.
C

reduce() + seen-map

Percorra uma vez, lembrando o que você já viu em um Map. Empurre um valor apenas na primeira vez — e escolha sua própria regra de "igual".

const unique = function (list, keyOf) {
  keyOf = keyOf || (function (v) { return v; });
  const seen = new Map();
  return list.reduce(function (out, v) {
    const k = keyOf(v);
    if (!seen.has(k)) { seen.set(k, 1); out.push(v); }
    return out;
  }, []);
};

Prós

  • +Rápida: uma passagem, linear como o Set.
  • +Chave customizada — desduplica objetos por um campo.

Contras

  • Mais código para ler; o mais fácil de errar sutilmente.
  • Exagero quando um Set simples bastaria.
Escolha isto quando Você precisa que "igual" signifique algo específico — ex.: usuários com o mesmo id — ou quer contar ao longo do caminho.
3

Qual delas devo escolher?


Escolha a linha que combina com a sua situação e a abordagem correspondente se acende abaixo, com uma frase sobre o porquê.

Minha lista é em sua maioria…

4

Em uma imagem


O verdadeiro divisor é como cada uma escala. À medida que a lista fica mais longa, a rota filter()+indexOf sobe muito mais rápido que as outras duas.

trabalho tamanho da lista → filter()+indexOf — íngreme (n²) new Set() — suave (n) reduce()+map — suave (n)
Leia da esquerda → direita: listas pequenas, todas vão bem. Listas longas, só o par linear continua barato.

Por que a curva vermelha verga para cima

filter() chama seu teste uma vez por item (são n chamadas), e o indexOf dentro dele varre o array de novo a cada vez (até n a mais) — então o trabalho total cresce como . O Set e o armazenamento de vistos do Map fazem uma única busca de O(1) médio por item, então o trabalho total cresce como n. Com 100 itens a diferença é invisível; com 100.000 a versão quadrática está fazendo bilhões de comparações.

Ordem & igualdade

Todas as três preservam a ordem do primeiro visto. O Set e a chave do Map usam SameValueZero, então NaN é desduplicado; o indexOf usa o === estrito, que nunca casa com NaN — então a abordagem B deixa NaNs duplicados. A abordagem C é a única que permite redefinir "igual" via keyOf.