Como usar o método reduce em Ruby

Tutoriais - 16/Out/2019 - por Allan Siqueira

Muita gente não conhece, ou até tem medo do método reduce . Neste artigo vou mostrar como ele pode ajudar a resolver de maneira eficiente problemas triviais, presentes no dia a dia de devs.

Neste exemplo vamos somar todos os salários (atributo salary) de um model Developer . Utilizando apenas os iteradores como each poderíamos fazer o seguinte:

developers = Developer.all
=> [#<Developer id: 1, salary: 2500>, #<Developer id: 1, salary: 2000>]
payroll = 0
developers.each do |developer|
  payroll += developer.salary
end
payroll
=> 4500

Ok, tudo funcionando, minha variável payroll(folha de pagamento) está com o valor esperado da soma dos salários.

Mas será que não há uma forma mais simples de resolver este problema?

Vamos realizar a mesma operação do exemplo acima utilizando o método reduce :

developers = Developer.all
=> [#<Developer id: 1, salary: 2500>,#<Developer id: 1, salary: 2000>]
developers.pluck(:salary).reduce(:+)
=> 4500

Muito mais limpo o código não é?

Utilizamos o pluck para isolar os valores do atributo que desejamos somar, neste caso o salary (aqui o pluck retornaria uma lista como esta: [2500, 2000] ).

O reduce vai "acumular" todos os valores da lista em que ele é chamado, aplicando o método passado como parâmetro, no caso o +.

Para entender melhor, veja o código abaixo:

salaries = [2000, 2500, 4000, 10000, 2000]
salaries.reduce do |accumulator, salary|
  accumulator + salary
end
=> 20500

Aqui novamente estamos executando a mesma operação, porêm de forma mais verbosa.

Podemos entender melhor o funcionamento do reduce: ele itera em todos os valores da lista, acumulando-os na variável accumulator (executando a operação desejada, neste caso + salary) e, no fim, retorna o valor da variável accumulator .

O reduce tem um comportamento complexo, e por isso, flexível para se adaptar a diferentes casos. Ele é uma ferramenta poderosa, se quiser se aprofundar mais em aplicações dela, recomendamos a documentação oficial deste método.

Em Ruby o reduce e o inject tem o mesmo funcionamento. A escolha por utilizar um ou outro, seria apenas uma questão de semântica, ou seja, se a ação que está sendo executada está mais próxima de uma "redução" (acumulação) ou de uma "injeção" (recurção).