The original iteration using Enumerable#each calculates the 5 factorial:
def factorial factorial_number
result = 1
(1..factorial_number).each{|number| result *= number }
result
end
factorial 5 => 120
can be refactored to the more descriptive:
def factorial factorial_number
(1..factorial_number).inject(1){|result, number| result *= number }
end
factorial 5 => 120
There are 2 points to be noticed.- you can initialise the iterators looping variable (result) value by just assigning it to inject
- inject returns the calculated iterators looping variable itself
(while Enumerable#each returns the array again)
Another example using the above mentioned factorial function puts all factorials into a Hash:
factorials = {}
[1, 2, 3, 4, 5].each do |number|
factorials[number.to_s] = factorial(number)
end
factorials => {"5"=>120, "4"=>24, "3"=>6, "2"=>2, "1"=>1}
which can be easily refactored to:
[1, 2, 3, 4, 5].inject({}) do |factorials, number|
factorials[number.to_s] = factorial(number)
factorials
end
=> {"5"=>120, "4"=>24, "3"=>6, "2"=>2, "1"=>1}
This refactoring may not as obvious as the first example in the first view. But consider you save an initialising line and you make sure to everyone what your intention is:you process an Enumerable (Array) and return an Enumerable (Hash) without having cumbersome code snippets. At least the Hash object doesn't need to be declared outside the iteration, which is far cleaner.
Be explicit in your code intentions!
There is an alias for Enumerable#inject, called Enumerable#reduce. It's really a matter of taste which one you prefer.
Supported by Ruby 2.1.0