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