Matheus Calegaro

(mais um) Blog sobre desenvolvimento e outras bobeiras.

Manipulando arrays com map, filter e reduce

Vamos supor que você tem um array que represente um carrinho de compras em uma loja online:

const carrinho = [
    {
        nome: 'Teclado',
        preco: 50.0,
        qtd: 1,
        embrulhoParaPresente: false
    },
    {
        nome: 'Mouse sem fio',
        preco: 39.90,
        qtd: 2,
        embrulhoParaPresente: false
    },
    {
        nome: 'Fone de Ouvido Chique',
        preco: 199.90,
        qtd: 1,
        embrulhoParaPresente: true
    }
]

O que você faz quando precisa iterar este array para, por exemplo, exibir os dados em uma <div> com id #carrinho? Creio que a primeira coisa que você pensaria seria algo como:

const elemCarrinho = document.querySelector('#carrinho')

for (let i = 0; i < carrinho.length; i++) {
    elemCarrinho.innerHTML += `${carrinho[i].nome} - R$ ${carrinho[i].preco} - Qtd.: ${carrinho[i].qtd}<br />`
}

Só que o JavaScript oferece alguns métodos próprios para essas situações e que deixarão seu código mais enxuto, como o:

forEach

carrinho.forEach((produto, index) => {
    elemCarrinho.innerHTML += `${index + 1}: ${produto.nome} - R$ ${produto.preco} - Qtd.: ${produto.qtd}<br />`
})

Se você já trabalhou com PHP, já entendeu o que está acontecendo no código acima. Caso contrário, deixa eu te explicar: o forEach pode ser lido como “para cada item de um array, execute uma função que irá fazer algo com cada item do array (o primeiro argumento da callback, que no nosso exemplo se chama produto só pra ficar de acordo com o nosso contexto de compras)”. Este método ainda suporta mais dois argumentos opcionais: o “index” (que retorna o índice da iteração atual) e o “array” (o próprio que estamos iterando).

Mas espera um pouco, um array não seria uma estrutura de dado? Como assim é possível encadear um método nele, ou pior, fazer o seguinte:

[1, 2, 3].forEach(callback)

Na verdade, o array no JavaScript é um objeto. Sim, um objeto!

WTF

Mais uma informação sobre o forEach é que ele retorna undefined, ou seja, não é possível fazer encadeamento de métodos (como nos outros métodos que eu vou falar aí em baixo).

Manipulando arrays

Chegamos na parte boa. Vamos conhecer os três métodos do título do post:

OBS: todos os métodos listados retornam um novo array contendo as manipulações realizadas.

Map

O map é um método mais generalizado para “massagear” os dados de um array. Assim como o forEach, devemos passar uma callback com três parâmetros (itemAtual, index e array) como o primeiro argumento do método.

const carrinhoComDesconto = carrinho.map(produto => {
    return {
        nome: produto.nome,
        preco: produto.preco - 10.0,
        qtd: produto.qtd,
        embrulhoParaPresente: produto.embrulhoParaPresente
    }
})

No exemplo acima, utilizamos o map para aplicar um desconto de R$10 em todos os produtos no carrinho. Repare no resultado que o novo array gerado possui o mesmo número de itens do array original.

Resultado do map

Filter

O que o filter faz é exatamente o que diz em seu nome: filtra um array baseado no que for escrito na função de callback.

const produtosParaPresente = carrinho.filter(produto => {
    if (produto.embrulhoParaPresente) return produto
})

O exemplo é bem similar ao do map, só que agora estamos fazendo uma verificação na função de callback para retornar apenas os produtos que estão marcados para serem embrulhados como presente.

Agora, repare que o número de itens retornados no novo array é apenas um. Isso ocorre porque o filter ignora os itens do array original que não passaram na condicional definida na callback.

Resultado do filter

Reduce

Este é um método que eu demorei um pouco para entender como funcionava, mas ele é realmente fácil (assim como os anteriores) e útil.

O reduce também faz o que seu nome diz: ele reduz um array para um único ítem, utilizando um acumulador para guardar o resultado de cada iteração. Mas como assim?

const precos = produto => produto.preco

const subtotalCarrinho = carrinho.map(precos).reduce((acumulador, itemAtual) => {
    return acumulador += itemAtual
})

Quebrando o código acima em etapas, temos:

  1. Criamos uma função chamada precos para ser usada no map e retornar apenas o preço
  2. Logo após, o reduce irá iterar sob os itens retornados pelo map
  3. A cada iteração do reduce, o valor de acumulador (que inicialmente não contém nada) será incrementado com itemAtual (ou seja, o preço do produto)

Legal, né? Olha o resultado:

Resultado do reduce

Conclusão

Pode não parecer, mas os métodos apresentados fazem uma grande diferença na hora de escrever JavaScript, principalmente quando a base de código começa a crescer e as manutenções começam a se tornar frequentes.

No início pode ser um pouco difícil de lembrar a sintaxe, mas nada que alguns treinos não resolva. Fique à vontade para usar o JSFiddle para “pintar e bordar” com o que foi aprendido hoje.

Grande abraço!

Comentários