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).