Repetir com controle, não no escuro

Loops executam blocos repetidamente — sobre coleções, até condição ou intervalo numérico. Processamento em lote (relatórios, importações, filas) depende de repetição correta e previsível.

for...of em coleções materializadas

const faturas = [
  { cliente: 'A', valor: 1200 },
  { cliente: 'B', valor: 890 },
  { cliente: 'C', valor: 2100 }
];

let total = 0;
for (const fatura of faturas) {
  total += fatura.valor;
}

for clássico quando índice importa

for (let i = 0; i < linhas.length; i++) {
  if (i % 2 === 0) {
    linhas[i] = linhas[i].trim();
  }
}

Off-by-one: i < length vs i <= length — erro clássico. Trace com três elementos manualmente.

while e paginação

async function buscarTodasPaginas(fetchPagina, paginaInicial = 1) {
  let pagina = paginaInicial;
  const acumulado = [];

  while (true) {
    const { itens, temProxima } = await fetchPagina(pagina);
    acumulado.push(...itens);
    if (!temProxima) break;
    pagina += 1;
  }

  return acumulado;
}
Diagrama PlantUML

map, filter, reduce

const funcionarios = [
  { nome: 'Rita', dept: 'TI', salario: 8000 },
  { nome: 'Paulo', dept: 'RH', salario: 6500 },
  { nome: 'Lia', dept: 'TI', salario: 9200 }
];

const folhaTi = funcionarios
  .filter(f => f.dept === 'TI')
  .map(f => ({ ...f, bonus: f.salario * 0.10 }))
  .reduce((acc, f) => acc + f.salario + f.bonus, 0);

break, continue e legibilidade

break interrompe loop; continue pula iteração. Uso excessivo indica loop candidato a refatoração (extrair função ou usar filter).

Performance consciente

  • Evite await dentro de loop grande sem controle de concorrência — pode saturar API.
  • Para milhões de registros, processamento streaming ou batch, não array gigante em memória.
  • Meça antes de otimizar; loops claros vencem micro-otimização prematura.

Estudo de caso: conciliação bancária simplificada

Dado extrato com centenas de lançamentos, marque duplicatas por hash de (data + valor + descrição normalizada):

function normalizarDescricao(texto) {
  return texto.trim().toUpperCase().replace(/\s+/g, ' ');
}

function encontrarDuplicatas(lancamentos) {
  const vistos = new Map();
  const duplicatas = [];

  for (const item of lancamentos) {
    const chave = `${item.data}|${item.valor}|${normalizarDescricao(item.descricao)}`;
    if (vistos.has(chave)) {
      duplicatas.push({ original: vistos.get(chave), duplicata: item });
    } else {
      vistos.set(chave, item);
    }
  }

  return duplicatas;
}

Este padrão combina loop, transformação de string e estrutura Map — três conceitos das semanas anteriores.

Quando evitar loop manual

Se a operação já existe na biblioteca padrão (ex.: Array.prototype.some, every, find), prefira expressividade. Loops manuais ficam para acumulações customizadas ou controle fino de fluxo.

Loops aninhados e complexidade

Dois loops aninhados sobre coleções de tamanho n e m tendem a O(n×m) operações. Para 1.000 × 1.000 itens, são um milhão de iterações — aceitável em script local, problemático em API síncrona. Antes de aninhar, pergunte: dá para indexar uma coleção em Map e reduzir a um loop?

// O(n×m) — evite em listas grandes
function paresNaive(a, b) {
  const resultado = [];
  for (const x of a) {
    for (const y of b) {
      if (x.id === y.id) resultado.push({ x, y });
    }
  }
  return resultado;
}

// O(n + m) — indexe primeiro
function paresComMap(a, b) {
  const indice = new Map(a.map(item => [item.id, item]));
  return b
    .filter(y => indice.has(y.id))
    .map(y => ({ x: indice.get(y.id), y }));
}

Esta refatoração aparece em entrevistas júnior/pleno: não é micro-otimização, é legibilidade + escala previsível.

Armadilhas comuns em while

  • Loop infinito: condição nunca muda — sempre defina variável de saída e incremento explícitos.
  • Mutar coleção durante iteração: remover item de array enquanto percorre com for...of pode pular elementos; filtre para novo array ou itere de trás para frente.
  • await em série desnecessário: buscar 50 URLs uma a uma é lento; batch com limite de concorrência quando a API permitir.

Exercício guiado: relatório de vendas por região

Implemente em três etapas, sem pular validação:

  1. Receba array de vendas { regiao, valor, data }.
  2. Filtre vendas do trimestre atual (compare mês/ano com Date).
  3. Agrupe por região com reduce e calcule total e ticket médio.
function relatorioTrimestre(vendas, ano, trimestre) {
  const mesInicio = (trimestre - 1) * 3;
  const mesFim = mesInicio + 2;

  const noPeriodo = vendas.filter(v => {
    const d = new Date(v.data);
    return d.getFullYear() === ano
      && d.getMonth() >= mesInicio
      && d.getMonth() <= mesFim;
  });

  return noPeriodo.reduce((acc, v) => {
    if (!acc[v.regiao]) {
      acc[v.regiao] = { total: 0, quantidade: 0 };
    }
    acc[v.regiao].total += v.valor;
    acc[v.regiao].quantidade += 1;
    return acc;
  }, {});
}

Teste com array vazio, uma região só, e venda na fronteira do trimestre (31/03 vs 01/04). Documente resultado esperado no seu README pessoal.

Do loop imperativo ao estilo declarativo

Em código de equipe, filter → map → reduce comunica intenção. Em hot path com milhões de registros, um for único pode ser mais eficiente — mas só após medir. Na fase de aprendizado, priorize clareza; o runtime moderno perdoa loops pequenos em scripts de estudo.

Conexão com a semana seguinte

Quando um loop não termina ou o total sai errado, você precisa ler erros e inspecionar variáveis — tema do próximo post. Antes de pedir ajuda externa, trace manualmente três iterações com lápis e papel: valor de i, item atual, acumulador.

Para aprofundar na web

Para entender melhor este tema, pesquise por:

  • "for loop while for...of JavaScript MDN" — quando usar cada tipo de repetição
  • "array map filter reduce explicado" — estilo declarativo sobre coleções
  • "off by one error loop programação" — erro clássico de limite < vs <=
  • "Big O notation introdução iniciante" — intuição sobre loops aninhados
  • "Map JavaScript estrutura chave valor" — indexação O(1) para lookups frequentes

Refaça o exercício de relatório por região usando apenas reduce — compare com loop for.

Atividades

  1. Off-by-one típico ocorre quando:

    • A) Usa-se for...of
    • B) Confunde i < n com i <= n
    • C) Usa-se const
    • D) Importa-se módulo ES
    Ver resposta

    Resposta correta: B) Confunde i < n com i <= n

    Limite inclusivo vs exclusivo altera quantidade de iterações em um.

  2. filter + reduce comunicam melhor:

    • A) Intenção de transformar e agregar coleções
    • B) Uso exclusivo de while true
    • C) Substituição de testes
    • D) Eliminação de objetos
    Ver resposta

    Resposta correta: A) Intenção de transformar e agregar coleções

    Estilo declarativo deixa explícito filtrar subset e reduzir a um valor.

  3. Quando preferir while em vez de for...of?

    Ver resposta

    Quando número de iterações não é conhecido antecipadamente: paginação até esgotar, leitura de stream, aguardar condição externa.