Todo programador já sabe que estruturas de repetição são essenciais em linguagens de programação. O while
, o for
e o do while
se tornam nossos companheiros inseparáveis logo no início da carreira. Além disso, eles são muito úteis quando queremos operar sobre vetores, certo? Mas, às vezes, podemos fazer melhor 😁. O objetivo deste post é apresentar métodos simples do objeto Array
em JavaScript que aplicam princípios de programação funcional e podem melhorar a legibilidade do seu código. A programação funcional é muito defendida no ambiente do JS por autores consagrados, como Eric Elliot e Kyle Simpson, e ganhou força com o EcmaScript 6, que já é vastamente suportado.
map, filter e reduce: os personagens principais
Todo array em JavaScript apresenta métodos de iteração utilitários para propósitos específicos. Aqui estão três mais relevantes:
Array.prototype.map()
recebe uma função de transformação a ser aplicada a cada elemento do array e retorna uma coleção com os elementos transformados por essa função.
const vetor = [1, 2, 3, 4]; const vetorDobrado = vetor.map(function(x) { return 2 * x; }); console.log(vetorDobrado) // 2, 4, 6, 8
O código fica mais legível com o uso de arrow functions👍:
const vetor = [1, 2, 3, 4]; const vetorDobrado = vetor.map(x => 2 * x); console.log(vetorDobrado) // 2, 4, 6, 8
Array.prototype.filter()
recebe uma função para filtragem do array. Ela deve retornar true
se o elemento passa no filtro e false
caso contrário.
const vetor = [1, 2, 3, 4]; const elementosPares = vetor.filter(x => x % 2 == 0); console.log(elementosPares) // 2, 4
Array.prototype.reduce()
é um pouco mais complexo, utilizado para reduzir o conteúdo de um array a um único valor. Ele recebe dois parâmetros: a função de redução, que será aplicada a cada elemento, e o valor inicial de redução.
Por sua vez, a função de redução deve receber dois argumentos principais: o valor reduzido até agora, e o valor do elemento sendo iterado.
Se quiser somar todos os elementos, por exemplo:
const vetor = [1, 2, 3, 4]; const soma = vetor.reduce((acumulado, atual) => acumulado + atual, 0); console.log(soma) // 10
No trecho acima, definimos que o valor inicial de redução é zero, e a cada iteração a função de redução deve retornar a soma do valor de redução acumulado com o valor do elemento daquela iteração. Na primeira iteração, realiza-se 0 + 1 = 1. Depois, 1 + 2 = 3. Em seguida, 3 + 3 = 6 e, por fim, 6 + 4 = 10
Juntando forças
Como map e filter retornam arrays, é possível encadear esses métodos. Isso possibilita que operações mais elaboradas sejam feitas de forma bem limpa. Essa prática é chamada piping. Em inglês, pipe significa “cano”, o que se assemelha ao fluxo dos dados quando são encadeados.
Agora vamos à um exemplo mais contextualizado. Imagine que você precise saber a quantidade de filhos das pessoas adultas em um array de pessoas:
const pessoas = [ { nome: 'Joaquim', idade: 30, quantidadeFilhos: 2, }, { nome: 'Ana', idade: 45, quantidadeFilhos: 3, }, { nome: 'Creuza', idade: 88, quantidadeFilhos: 8, }, ];
Supondo que um adulto é alguém com menos de 65 anos, pode-se realizar essa tarefa da seguinte maneira:
const IdadeIdosos = 65; const quantidadeFilhosDosJovens = pessoas .filter(pessoa => pessoa.idade < IdadeIdosos) .map(pessoa => pessoa.quantidadeFilhos) .reduce((quantidadeTotal, quantidade) => quantidadeTotal + quantidade, 0); console.log(quantidadeFilhosDosJovens); // 5
Quando a estrutura do map, filter e reduce já estiverem familiares, você perceberá que o código se torna muito mais legível do que a versão imperativa:
const IdadeIdosos = 65; let quantidadeFilhosDosJovens = 0; for (let i = 0; i < pessoas.length; i++) { if (pessoas[i].idade < IdadeIdosos) { quantidadeFilhosDosJovens += pessoas[i].quantidadeFilhos; } } console.log(quantidadeFilhosDosJovens); // 5
Chique, né? E se você quiser o máximo da legibilidade, basta atribuir as funções utilizadas a variáveis e repassá-las aos métodos que foram aprendidos:
const IdadeIdosos = 65; const ehJovem = pessoa => pessoa.idade < IdadeIdosos; const obterQuantidadeFilhos = pessoa => pessoa.quantidadeFilhos; const somar = (quantidadeTotal, quantidade) => quantidadeTotal + quantidade; const quantidadeFilhosDosJovens = pessoas .filter(ehJovem) .map(obterQuantidadeFilhos) .reduce(somar, 0); console.log(quantidadeFilhosDosJovens); // 5
Existem outros métodos do objeto Array
que também contornam a necessidade de utiliza-se um laço for
. Confira na página da MDN os métodos .find()
, .some()
, .every()
e .includes()
para aprender mais.
Conclusão
O ES6 trouxe novas maneiras de operar sobre arrays. Esses novos caminhos são mais legíveis, por serem mais declarativos, melhoram a reutilização de código e utilizam toda a força da característica multi-paradigma do JavaScript. Com todas essas novas ferramentas em mãos, são pouquíssimos os casos em que a utilização de um for
nativo é imprescindível.