DSC05677
Share on facebook
Share on twitter
Share on linkedin
Diurno

JavaScript – O laço for: um personagem secundário

 

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.