Entendendo A Função Reduce - Parte 2
A parte 1 do artigo se encontra aqui. Entendendo A Função Reduce - Parte 1
Eu dei uma relida no artigo sobre reduce que tinha publicado anteriormente, e percebi algumas coisas que eu gostaria de adicionar, vamos lá. Se você não leu o artigo anterior, eu recomendo ler.
Tipos de retorno no reduce
O reduce recebe uma lista do tipo X
(onde X
pode ser um objeto, um número, qualquer coisa) e geralmente te retorna um único valor do tipo X
, vamos dizer que isso é do tipo [X] -> X
.
Mas isso não é obrigatório. É bem possível que o reduce te retorne um outro tipo (nesse caso seria [X] -> Y
), ou seja, pode receber uma lista de inteiros e retornar uma string ou um objeto, por exemplo.
Vou exemplificar com código logo abaixo. Aqui eu tenho duas funções. Na primeira eu recebo uma lista de números, e retorno um único numero (que é a soma de todos os valores dessa lista). Isso seria [X] -> X
.
Mas na segunda função, eu recebo uma lista de strings e retorno uma lista de objetos, isso seria [X] -> Y
.
|
|
No Javascript, geralmente, se você chama seu reduce sem passar um valor inicial, ele será do tipo [X] -> X
. Já se você quiser fazer algo do tipo [X] -> Y
, geralmente, terá que passar um valor inicial, que é o segundo parametro da função reduce, logo após a sua função anônima. No exemplo acima eu coloquei um array vazio.
Debuggando o reduce
Bom, a ideia aqui é criar um reduce, e ir passo a passo pra ver o que ele está fazendo. Primeiro vou reimplementar a função somador e ver como os valores vão mudando a cada passo, como aconteceria num debugger mesmo. Depois explicarei um exemplo um pouco mais complexo (e útil).
|
|
Agora vamos lá. Se você não passar um valor inicial (como eu não passei), o JS irá pegar o primeiro item do array pra ir pro acc
, e vai começar a rodar a partir do segundo, ou seja, vai colocar o segundo item no value
. Então dando um passo e nosso estado seria isso aqui
|
|
O resultado dessa soma irá sobrescrever o valor de acc, que passará a ser 3. Vamos dar mais um passo. Atualmente o nosso acc
é um 3, e o nosso value
irá pegar o valor do próximo item do array. Que por coincidência é um 3 também
|
|
Certo, agora o nosso acc
vale 15, e a nossa lista está vazia, o que acontece agora? Bom, quando a lista está vazia, o reduce simplesmente pega o que está no acc
e retorna para o usuário. É por isso que o exemplo abaixo retorna o valor 1, e não o valor 500.
|
|
Resolvendo um problema possivelmente real
Bom, agora vem o “debugging” mais complexo que citei anteriormente. Imaginemos que você recebe uma lista de objetos que representam a pontuação dos usuários, e você quer definir qual usuário ganhou. Existem várias formas de resolver esse problema, e uma delas é o reduce.
|
|
Agora que nós temos a lista de pontuações, é só rodar no nosso reduce
|
|
Como eu não quero “acumular” um valor nesse caso, eu preferi chamar o meu primeiro parâmetro como “previousValue” (valorAnterior) e o segundo como “actualValue” (valoarAtual).
A ideia aqui é basicamente olhar se o meu actualValue
tem um score maior que o previousValue
, se ele tiver, eu retorno o actualValue
, sobrescrevendo o que tem no previousValue
.
Agora, caso o score do meu previousValue
seja maior ou igual ao do meu actualValue
eu retorno o previousValue
, ou seja, eu basicamente estou deixando o mesmo valor que tinha no previousValue
. Não estou tratando o caso de empate aqui.
Coloquei esse else pra deixar claro que retorna um ou outro, nesse caso ele é opcional.
|
|
Como o score do meu valor atual é maior do que o do valor anterior, eu retorno o meu valor atual, sobrescrevendo o previousValue
com ele. Agora o nosso próximo passo está assim.
|
|
E como o meu valor atual não tem um score maior do que o anterior, vamos retornar o valor anterior para ser salvo no nosso acumulador (ou previousValue
nesse caso), basicamente deixando do mesmo jeito que estava.
O reduce irá rodar novamente e verá que a lista está vazia, aí ele simplesmente retornará o que tiver no meu acumulador. E nesse caso isso é a pontuação do zezinho. { name: 'Zezinho', score: 25}
.
Se você quiser tirar a prova, abra o node ou o console do navegador e rode o código completo abaixo
|
|
Conclusão
Bom, esse artigo serviu mais para explicar melhor como o reduce computa os valores da lista e reduz um [X]
em outra coisa como um X
ou um Y
.
Ainda existem muitas coisas para serem abordadas, por exemplo: como map e filter são derivados do reduce, como é uma recursão por trás do reduce, como usar os outros dois parâmetros da função interna do reduce que nós ainda não usamos.