map, filter e reduce
Esta aula aborda os métodos map, filter e reduce do JavaScript, explicando como transformar, filtrar e acumular dados em arrays, além de mostrar como encadear esses métodos para operações complexas de forma funcional e eficiente.
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); // 10Se 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,filterereducea loopsforouforEachquando 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
reducepara 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 demapseguido deflatpara achatar arrays aninhados.
Referências
- MDN: Array.prototype.map()
- MDN: Array.prototype.filter()
- MDN: Array.prototype.reduce()
- MDN: Array.prototype.flatMap()
- JavaScript.info: Array methods
- Eloquent JavaScript: Higher-Order Functions
Exercícios
Dado o array
[1, 2, 3, 4, 5], usemappara 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]Dado o array
['Ana', 'Bruno', 'Carla', 'Daniel'], usefilterpara 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']Dado o array
[10, 20, 30, 40], usereducepara 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); // 240000Dado o array de objetos
[{ nome: 'Teclado', preco: 100 }, { nome: 'Mouse', preco: 50 }, { nome: 'Monitor', preco: 300 }], use encadeamento defilteremappara 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']Dado o array
[1, 2, 3, 4, 5, 6], use encadeamento defilter,mapereducepara: 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