No artigo anterior falamos sobre reduce: aprendemos o que é e como implementá-lo. Além disso, vimos como usá-lo para criar outras funções presentes em programação funcional.
Hoje vamos aprender como implementar mais algumas funções (map, filter e flatMap) utilizando apenas a função reduce, e como um bônus vamos ver como podemos usar a função reduce para garantir a ordem sequencial de promises em JavaScript.
Implementar uma função é a melhor maneira de aprender como usá-la, portanto vamos pular logo para a implementação.
Map
Nossa função map receberá dois parâmetros: um array e uma função. O resultado dela é um novo array contendo o resultado da execução da função passada a cada elemento do array passado como parâmetro.
Implementando map usando somente a função reduce:
const map = function(iteravel,fn){
return reduce(iteravel,(acc,n)=>acc.concat([fn(n)]),[])
}
console.log(map([1,2,3],i=>i+1)) //Imprime [2,3,4]
Esta certamente não é a melhor implementação para a função map. Nós vamos usá-la somente para mostrar o quão poderoso é o reduce.
Para implementar a função map, nós chamamos a função reduce passando um dado array, uma função e um array vazio, que vai ser usado como acumulador e retornado pela função map.
A função passada como segundo parâmetro do reduce é bastante simples: ela só acumula no novo array o resultado da execução da função de mapeamento (no nosso exemplo i => i +1) para cada elemento do array passado como parâmetro pra ela.
Filter
A função filter também recebe dois parâmetros, um array, e uma função contendo uma condição. O resultado dela é um novo array contendo somente os elementos que satisfazem aquela condição:
const filter = function(iteravel,fn){
return reduce(iteravel,(acc,n)=>fn(n)?acc.concat([n]):acc,[])
}
console.log(filter([1,2,3,4],i=>i<3)) // Imprime [1,2]
Como você pode ver, a implementação é similar a utilizada no map. Agora, porém, a função passada para o reduce verifica se o elemento atual satisfaz uma dada condição, e se ela é satisfeita o coloca no resultado.
FlatMap
Vejamos o seguinte código:
console.log(map([1,2],i=>[i,i+1])) //Imprime [[1,2],[2,3]]
Quando estamos usando a função map, se o resultado da função passada como parâmetro também for um array, o resultado será um array de arrays. Se o que queremos é um array simples (com somente um nível), nós utilizamos a função flatMap.
Colocando de maneira objetiva, a função flatMap faz o mapeamento e entrega como resultado um array de um só nível.
A implementação também é bastante simples:
const flatMap = function(iteravel,fn){
return reduce(iteravel,(acc,n)=>acc.concat(fn(n)),[])
}
console.log(flatMap([1,2],i=>[i,i+1]))//Imprime [1,2,2,3]
A implementação é similar ao map. A única mudança é que nós fazemos concat(fn(n)) ao invés de concat([fn(n)]), porque no primeiro caso, se o resultado de fn(n) é um array, ele vai concatenar com o acumulador, mantendo a estrutura com apenas um nível.
Promises
Por último, um outro uso muito legal da função reduce é para executar promises uma depois da outra e acumular o resultado.
const somaPromises = function(promiseFunctions){
return reduce(promiseFunctions,async (accPromise,proximo)=>{
const acumulador = await accPromise
const resultadoAtual = await proximo()
return acumulador + resultadoAtual
},Promise.resolve(0))
}
const promiseFunctions = [
()=> Promise.resolve(1),
()=> Promise.resolve(2),
()=> Promise.resolve(3)
]
somaPromises(promiseFunctions).then(console.log)// Imprime 6
A função somaPromises recebe um array de funções assíncronas. Nós utilizamos a função reduce para iterar sobre este array, passando também uma função assíncrona.
Esta função assíncrona aguarda (await) o resultado da última promise e guarda o resultado na constant acumulador, e então aguarda (await) pela próxima promise, gravando o resultado na constante resultadoAtual e finalmente retorna o resultado da soma.
Perceba que estamos usando uma função assíncrona (async) e portanto o seu resultado é também uma promise, que será utilizada como acumulador da próxima iteração. Nós inicializamos o acumulador da função reduce com uma promise contendo o valor 0.
Espero que tenham gostado e aprendido um pouco mais sobre a função reduce e formas de utilizá-la.