Trabalhar com arrays em JavaScript raramente significa que você precisa escrever um loop for hoje em dia. A linguagem agora oferece ferramentas expressivas que transformam dados aninhados e ruidosos em transformações limpas e legíveis. Este guia continua de onde os conceitos básicos param — focando em achatar (flattening), reduzir (reducing), consultar e nos novos padrões imutáveis que mantêm seus dados originais intactos.
Achatando Estruturas Aninhadas com flat() e flatMap()
Dados do mundo real adoram aninhamento. Ao buscar registros de uma API, analisar um CSV ou ler um arquivo de configuração, você frequentemente acabará com arrays dentro de arrays. O flat() existe para colapsar essas camadas em uma única lista linear.
Chame flat() em um array aninhado e, por padrão, ele achatará apenas um nível de profundidade:
const nested = [[1, 2], [3, 4]];
const flat = nested.flat();
// [1, 2, 3, 4]
O array nested original permanece exatamente como estava. Se o seu aninhamento for mais profundo, passe um argumento de profundidade como flat(2) ou até mesmo flat(Infinity) quando não souber quantos níveis existem. Isso é melhor do que escrever funções auxiliares recursivas manualmente.
Quando você precisa transformar elementos e achatar o resultado, o flatMap() faz ambos em uma única passagem. Ele executa uma função de mapeamento em cada elemento e, em seguida, achata os arrays retornados em um nível. Fazer isso com chamadas separadas de .map().flat() funciona, mas o flatMap() pula o array intermediário, o que economiza memória e uma segunda iteração. Um caso de uso clássico é dividir um array de frases em palavras individuais:
const lines = ['hello world', 'foo bar'];
const words = lines.flatMap(line => line.split(' '));
// ['hello', 'world', 'foo', 'bar']
Filtragem e Mapeamento: Saiba a Diferença
O filter() cria um novo array contendo apenas os itens que passam no seu teste. O callback deve retornar um valor truthy para manter o elemento, ou um valor falsy para descartá-lo. Ele nunca muta o array de origem.
const users = [
{ name: 'Ajay', active: true },
{ name: 'Bina', active: false }
];
const activeUsers = users.filter(u => u.active);
É fácil confundir filter() com map() porque ambos retornam novos arrays e aceitam callbacks. A diferença está no contrato. O map() retorna um novo array com o mesmo comprimento. Transforme cada elemento, execute uma verificação condicional e você verá, de fato, true ou false para cada posição, porque o map() nunca descarta itens — ele apenas os altera. O filter(), por outro lado, retorna um array potencialmente menor contendo apenas os elementos que passam no seu teste. Se você quiser remover dados, use o filter(). Se quiser transformar cada dado preservando a contagem, use o map().
Colapsando Arrays com reduce()
Enquanto map() e filter() retornam arrays, o reduce() destila uma lista inteira em um único valor. Ele percorre da esquerda para a direita, carregando um acumulador através de cada etapa.
const totals = [10, 20, 30];
const sum = totals.reduce((acc, current) => acc + current, 0);
// 60
O segundo argumento — 0 no exemplo acima — é o valor inicial do acumulador. Se você o omitir, o JavaScript usará o primeiro elemento como ponto de partida, iniciando a iteração no índice 1. Isso costuma confundir as pessoas quando o array está vazio, pois chamar reduce() sem um valor inicial em um array vazio lança um TypeError.
O reduce não serve apenas para aritmética. Você pode usá-lo para construir tabelas de frequência, mesclar objetos, encontrar valores máximos ou passar dados por múltiplas transformações. Como ele é muito flexível, vale a pena manter o callback pequeno e nomear suas variáveis de forma clara. Um bloco reduce() denso torna-se rapidamente mais difícil de ler do que o loop que ele substituiu.
Consultas Booleanas: every() e some()
Às vezes, você não precisa de um novo array; você precisa de uma resposta de "sim ou não" sobre a coleção. O every() retorna true apenas se todos os elementos satisfizerem sua condição. Se o primeiríssimo elemento falhar, ele para imediatamente e retorna false.
const scores = [85, 92, 78];
const allPassed = scores.every(s => s >= 50);
O some() é a imagem espelhada. Ele retorna true se pelo menos um elemento passar no teste. Encontrou uma correspondência, e ele interrompe a execução na hora.
const hasPerfectScore = scores.some(s => s === 100);
Esses métodos substituem loops manuais para verificações de validação. Precisa confirmar se todos os campos do formulário estão preenchidos? Use every(). Precisa saber se algum usuário tem direitos de administrador? Use some().
Inspecionando e Atualizando Sem Mutação
O JavaScript moderno baseia-se fortemente na imutabilidade. Esses métodos ajudam você a inspecionar ou atualizar arrays sem tocar no original.
keys() retorna um objeto iterador contendo os índices do array. entries() retorna um iterador de pares [index, value]. Eles brilham em loops for...of quando você deseja uma desestruturação limpa:
const fruits = ['apple', 'banana'];
for (const [index, fruit] of fruits.entries()) {
console.log(index, fruit);
}
with() é uma adição mais recente à linguagem que substitui um elemento em um índice específico e retorna um array totalmente novo. Ele atende à mesma necessidade da atribuição por colchetes — arr[index] = value — exceto que deixa o array de origem intacto. Em frameworks como o React, onde a mutação direta quebra a detecção de mudanças, o with() é o caminho mais seguro:
const original = [10, 20, 30];
const updated = original.with(1, 99);
// original: [10, 20, 30]
// updated: [10, 99, 30]
Spread e Rest: Duas Direções de ...
A sintaxe spread e o parâmetro rest compartilham os mesmos três pontos, mas movem os dados em direções opostas.
Use o operador spread para expandir um array em elementos individuais. É a maneira mais limpa de mesclar arrays ou cloná-los:
const front = [1, 2];
const back = [3, 4];
const merged = [...front, ...back];
const copy = [...merged];
Você também pode adicionar itens extras facilmente: [0, ...front, 99, ...back].
O parâmetro rest agrupa múltiplos elementos em um único array. Dentro de uma assinatura de função, ele coleta quaisquer argumentos que restarem:
function logFirst(first, ...rest) {
console.log('First:', first);
console.log('Rest:', rest);
}
logFirst('a', 'b', 'c');
// First: a
// Rest: ['b', 'c']
Juntos, eles lidam com a tarefa comum de aceitar entradas variádicas, processá-las como um array e, em seguida, espalhá-las novamente em chamadas de função ou novas estruturas de dados.
A Grande Lição
O objetivo desses métodos não é impressionar colegas de equipe com linhas de código únicas. É tornar sua intenção óbvia. Quando outro desenvolvedor vê filter(), ele sabe que você está removendo itens. Quando vêem reduce(), sabem que você está consolidando dados. Quando vêem with(), sabem que o array original ainda está intacto em algum lugar. Dominar essas ferramentas significa escrever código que se explica sozinho, e isso economiza muito mais tempo do que qualquer loop inteligente jamais fará.
