JavaScript

28 set, 2018

Implementando Reduce em JavaScript – Parte 02

Publicidade

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.