Os métodos map, filter e reduce são ferramentas fundamentais para manipulação de arrays em JavaScript, permitindo operações de transformação, filtragem e acumulação de dados de forma declarativa e imutável. Eles fazem parte do paradigma de programação funcional e são amplamente utilizados em código moderno para evitar loops explícitos e tornar o código mais legível e conciso.

Nesta aula, exploraremos cada um desses métodos em detalhes, com exemplos práticos, e veremos como combiná-los em encadeamentos para resolver problemas complexos de maneira elegante.

Transformação

O método map cria um novo array com os resultados da chamada de uma função para cada elemento do array original. Ele é usado para transformar os dados de um array sem modificar o original, mantendo a imutabilidade. A função passada para map recebe três argumentos: o valor atual, o índice e o próprio array.

Por exemplo, suponha que temos um array de números e queremos criar um novo array com o dobro de cada número:

const numeros = [1, 2, 3, 4];
const dobrados = numeros.map(num => num * 2);
console.log(dobrados); // [2, 4, 6, 8]

map é especialmente útil para extrair propriedades de objetos em um array. Por exemplo, se temos uma lista de usuários e queremos apenas seus nomes:

const usuarios = [
  { id: 1, nome: 'Ana' },
  { id: 2, nome: 'Bruno' },
  { id: 3, nome: 'Carla' }
];
const nomes = usuarios.map(usuario => usuario.nome);
console.log(nomes); // ['Ana', 'Bruno', 'Carla']

É importante notar que a função callback deve ser pura (sem efeitos colaterais) para garantir previsibilidade. Além disso, o array original permanece inalterado.

Filtragem

O método filter cria um novo array com todos os elementos que passam em um teste implementado pela função fornecida. Ele é usado para selecionar subconjuntos de dados baseados em condições. A função callback deve retornar true para incluir o elemento no novo array, ou false para excluí-lo.

Por exemplo, para filtrar números pares de um array:

const numeros = [1, 2, 3, 4, 5, 6];
const pares = numeros.filter(num => num % 2 === 0);
console.log(pares); // [2, 4, 6]

Outro exemplo comum é filtrar objetos por uma propriedade. Suponha que temos uma lista de produtos e queremos apenas os que estão em estoque:

const produtos = [
  { nome: 'Camiseta', estoque: true },
  { nome: 'Calça', estoque: false },
  { nome: 'Jaqueta', estoque: true }
];
const disponiveis = produtos.filter(produto => produto.estoque);
console.log(disponiveis); // [{ nome: 'Camiseta', estoque: true }, { nome: 'Jaqueta', estoque: true }]

Assim como map, filter não modifica o array original e retorna um novo array. Se nenhum elemento passar no teste, o array resultante será vazio.

Acumulação

O método reduce executa uma função redutora para cada elemento do array, resultando em um único valor de saída. Ele é extremamente versátil e pode ser usado para somar valores, concatenar strings, agrupar objetos, entre outras operações de acumulação. A função recebe quatro argumentos: acumulador, valor atual, índice e array. Além disso, reduce aceita um valor inicial opcional para o acumulador.

Por exemplo, para somar todos os números de um array:

const numeros = [1, 2, 3, 4];
const soma = numeros.reduce((acumulador, valorAtual) => acumulador + valorAtual, 0);
console.log(soma); // 10

Se o valor inicial não for fornecido, o primeiro elemento do array é usado como acumulador e a iteração começa a partir do segundo elemento. No entanto, é uma boa prática sempre fornecer o valor inicial para evitar erros com arrays vazios.

Outro exemplo: transformar um array de pares chave-valor em um objeto:

const pares = [['nome', 'Maria'], ['idade', 30], ['cidade', 'São Paulo']];
const objeto = pares.reduce((acc, [chave, valor]) => {
  acc[chave] = valor;
  return acc;
}, {});
console.log(objeto); // { nome: 'Maria', idade: 30, cidade: 'São Paulo' }

reduce é poderoso, mas pode ser menos legível que combinações de map e filter. Use com moderação e documente a lógica quando necessário.

Encadeamento

Uma das grandes vantagens desses métodos é a possibilidade de encadeá-los, criando pipelines de processamento de dados. Como cada método retorna um novo array, podemos chamar sucessivamente map, filter e reduce em sequência. Isso torna o código mais expressivo e fácil de entender.

Por exemplo, suponha que temos um array de números e queremos: 1) filtrar apenas os pares, 2) dobrar cada um, e 3) somar todos. Podemos fazer:

const numeros = [1, 2, 3, 4, 5, 6];
const resultado = numeros
  .filter(num => num % 2 === 0)
  .map(num => num * 2)
  .reduce((acc, num) => acc + num, 0);
console.log(resultado); // 24 (pares: 2,4,6; dobrados: 4,8,12; soma: 24)

Outro exemplo prático: a partir de uma lista de pedidos, queremos calcular o valor total dos pedidos de clientes premium que estão ativos:

const pedidos = [
  { cliente: 'Ana', tipo: 'premium', ativo: true, valor: 100 },
  { cliente: 'Bruno', tipo: 'normal', ativo: true, valor: 50 },
  { cliente: 'Carla', tipo: 'premium', ativo: false, valor: 200 },
  { cliente: 'Daniel', tipo: 'premium', ativo: true, valor: 150 }
];
const totalPremiumAtivo = pedidos
  .filter(p => p.tipo === 'premium' && p.ativo)
  .map(p => p.valor)
  .reduce((acc, valor) => acc + valor, 0);
console.log(totalPremiumAtivo); // 250 (Ana: 100 + Daniel: 150)

Ao encadear, a ordem das operações importa: geralmente, filter primeiro para reduzir o número de elementos, depois map para transformar, e reduce no final para agregar. Isso otimiza a performance e mantém a clareza.

Boas Práticas

  • Prefira map, filter e reduce a loops for ou forEach quando a intenção for transformar, filtrar ou acumular dados.
  • Mantenha as funções callback puras: sem modificar variáveis externas ou causar efeitos colaterais.
  • Sempre forneça um valor inicial para reduce para evitar erros com arrays vazios.
  • Evite encadeamentos muito longos; se necessário, quebre em etapas com nomes descritivos.
  • Use flatMap (disponível no ES2019) como combinação de map seguido de flat para achatar arrays aninhados.

Referências

Exercícios

  1. Dado o array [1, 2, 3, 4, 5], use map para criar um novo array com o quadrado de cada número.

    ✓ Resposta:
    const numeros = [1, 2, 3, 4, 5];
    const quadrados = numeros.map(num => num * num);
    console.log(quadrados); // [1, 4, 9, 16, 25]
  2. Dado o array ['Ana', 'Bruno', 'Carla', 'Daniel'], use filter para retornar apenas os nomes com mais de 4 letras.

    ✓ Resposta:
    const nomes = ['Ana', 'Bruno', 'Carla', 'Daniel'];
    const nomesLongos = nomes.filter(nome => nome.length > 4);
    console.log(nomesLongos); // ['Bruno', 'Carla', 'Daniel']
  3. Dado o array [10, 20, 30, 40], use reduce para calcular o produto de todos os números.

    ✓ Resposta:
    const numeros = [10, 20, 30, 40];
    const produto = numeros.reduce((acc, num) => acc * num, 1);
    console.log(produto); // 240000
  4. Dado o array de objetos [{ nome: 'Teclado', preco: 100 }, { nome: 'Mouse', preco: 50 }, { nome: 'Monitor', preco: 300 }], use encadeamento de filter e map para obter um array com os nomes dos produtos com preço maior que 60.

    ✓ Resposta:
    const produtos = [
      { nome: 'Teclado', preco: 100 },
      { nome: 'Mouse', preco: 50 },
      { nome: 'Monitor', preco: 300 }
    ];
    const nomesCaros = produtos
      .filter(p => p.preco > 60)
      .map(p => p.nome);
    console.log(nomesCaros); // ['Teclado', 'Monitor']
  5. Dado o array [1, 2, 3, 4, 5, 6], use encadeamento de filter, map e reduce para: filtrar os números ímpares, elevar ao cubo e depois somar todos.

    ✓ Resposta:
    const numeros = [1, 2, 3, 4, 5, 6];
    const resultado = numeros
      .filter(num => num % 2 !== 0)
      .map(num => Math.pow(num, 3))
      .reduce((acc, num) => acc + num, 0);
    console.log(resultado); // 1 + 27 + 125 = 153