6 métodos importantes em Arrays de JavaScript
Explicando reduce, map, filter, find, every e some
Ultimamente, muito se tem falado em programação funcional e suas vantagens. Algumas linguagens com foco puramente funcional, como Haskell e Erlang, estão recebendo bastante atenção. Entretanto, suas técnicas e estilo podem ser utilizados na maioria das linguagens, como no nosso bom e velho amigo JavaScript (ok vai, nem tão velho assim…). Não entrarei em detalhes sobre programação funcional, pois já existe uma penca de artigos falando sobre isso.
O principal conceito utilizado será high-order function, que é basicamente uma função que recebe uma ou mais funções como argumentos ou retorna uma função. Neste artigo mostrarei como utilizar a high-order functionreduce
e outras 5 variações, como map
, filter
, some
, every
e o find
. Com “variações” quero dizer que todos estes métodos são, de algum modo, derivados do reduce
, pois ele é capaz de fazer tudo o que estes outros fazem.
Uma característica marcante destes métodos que vale a pena ser citada é que elas não modificam o valor original da variável, seguindo uma outra característica importante da programação funcional: imutabilidade.
Nota: os códigos foram escritos na sintaxe ES6.
Mas afinal, do que o reduce
é capaz?
O reduce
é um método de Array.prototype
, (assim como todos os outros citados acima) e sua função é transformar ou reduzir (sacou? reduzir, reduce…) os valores do array a um valor diferente do original.
O primeiro argumento do reduce
é uma função (por isso é uma high-order function) que recebe como argumentos um acumulador, que agrega o resultado das iterações anteriores, e o item atual do array.
O segundo argumento é o valor inicial do acumulador, que neste caso é 0
. É este argumento que define qual é o tipo do valor retornado, no caso um número inteiro.
Este foi um exemplo bem simples… afinal, não é todo dia que você precisa somar um array com 5 elementos, certo? Aguenta aí, pois nos próximos tópicos veremos como utilizar cada um das variações citadas e como seria sua versão com o reduce
, que geralmente é mais verbosa.
Nota: O array abaixo (personagens de vídeo game e suas respectivas datas de estreia) será utilizado nos próximos exemplos.
const characters = [
{ name: "Donkey Kong", debut: 1981 },
{ name: "Captain Falcon", debut: 1990 },
{ name: "Fox", debut: 1993, },
{ name: "Kirby", debut: 1992 },
{ name: "Link", debut: 1986 }
];
.filter()
Como o próprio nome diz, este método é utilizado para filtrar os elementos de um array através de uma condicional executada dentro da função fornecida como argumento.
Note no exemplo acima como o métodofilter
exige somente que seu primeiro argumento seja uma função, que retorna o resultado de uma expressão condicional, enquanto que na versão com reduce
é necessário que deixemos explícito que, quando a condicional for verdadeira, o item atual da iteração seja adicionado ao acumulador. O resultado é o mesmo, porém filter
é muito mais expressivo!
.map()
O map
é um reprodutor de arrays adulteradas! Seu resultado é sempre um array com o mesmo tamanho do original, mas com pequenas (ou grandes) transformações.
Novamente, é importante notar como as variações são bem mais expressivas que sua versão com reduce
. No exemplo acima, a propriedade name
foi extraída do objeto de cada elemento, resultando em um array diferente do original.
.some()
Em algum momento você já precisou verificar se, no seu array, existe pelo menos um elemento que cumpra uma condição. Geralmente, o primeiro impulso é usar filter
e verificar se o tamanho do array resultante é maior que 0, como no código abaixo.
É exatamente para este tipo de situação que some
existe. Ele elimina a necessidade de utilizar filter
e esta verificação do tamanho do array resultante.
Qualquer semelhança com o filter
não é mera coincidência. A diferença é o tipo de retorno, que neste caso é um boolean.
.every()
Bastante parecido com seu primo some
, só que mais exigente. Oevery
requer que todos os elementos do array cumpram com sua condição.
Algumas coisas diferentes estão acontecendo no código acima. Na linha 1, a função hasDebutAfter1980
será reaproveitada no resto do código para verificar se a estreia do personagem foi depois de 1980. E na linha 3, é utilizada como argumento para o every
. Da mesma forma, nas linhas 6 e 10 para verificar a data de estreia do personagem da iteração atual dentro do reduce
.
.find()
Eu costumo pensar que o find
é um filter
preguiçoso… ele busca por um elemento de acordo com a condição, e o primeiro encontrado já é retornado e pronto: missão cumprida.
Observe que, apesar de todos os personagens terem sua data de estreia após 1980, somente o primeiro é retornado.
Unindo forças
Como foi dito anteriormente, os métodos citados nos tópicos acima fazem parte de Array.prototype
, portanto eles sempre estarão disponíveis numa variável do tipo Array
. Repare que as funções filter
e map
sempre retornam um array, fazendo com que seja possível um encadeamento de métodos (method chaining).
Obviamente que o mesmo resultado pode ser obtido utilizando reduce
, mas na minha humilde opinião, a combinação de métodos torna o código mais legível e incentiva a modularização, tornando-o mais fácil de ser reaproveitado e testado.
No exemplo acima, a implementação da função que verifica se o personagem estreou nos anos 90 e a que extrai o nome do personagem foram atribuídas a uma variável e passadas como argumento para o filter
e o map
, respectivamente, e utilizadas separadamente em outros contextos.
A combinação destes métodos (não somente de filter
e map
) é realmente bastante poderosa!
Conclusão
Entender como extrair a informação correta ou manipulá-la de acordo com o contexto de sua aplicação é extremamente importante quando se lida, por exemplo, com ferramentas de visualização de dados, ou extrair somente uma parte da informação retornada por uma API. No mundo real, nem sempre estes dados estarão bem estruturados ou num formato amigável. Por este motivo é importante que nosso código seja o mais claro e legível possível, e com a ajuda dos métodos citados neste artigo fica mais fácil alcançar este objetivo. Nada contra for
e forEach
, mas chega de usá-los pra tudo nessa vida! ;)