Sonntag, 23. Februar 2014

Inject readability! ... a Ruby iteration.

Ruby's Enumerable offers a bunch of iterators for dedicated tasks. One of them is inject (Enumerable#inject Ruby API). It provides you with power and the ability to write concise and meanful code. And it's just readable.
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.
  1. you can initialise the iterators looping variable (result) value by just assigning it to inject
  2. inject returns the calculated iterators looping variable itself
    (while Enumerable#each returns the array again)
And by the way, you even could leave the initialising value (1) in this example.
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