I was rather taken with inject when I first started getting serious with Ruby. It allows you to turn ugly, imperative-style loops requiring temporary variables into functional-style expressions. Like this:
total = 0
items.each do |item|
total += item.price * item.quantity
end
total
Becomes:
items.inject(0) do |total, item|
total += item.price * item.quantity
end
Much better. (Jay Fields expands on this, in case you’re not already an inject junkie.)
And yet, somehow, this hasn’t always sat quite right with me. I had to look up the syntax quite a bit when I was first learning it (I always wanted to do |item, total| rather than the reverse), and from time to time my use of inject seems to create subtle bugs that take me a while to figure out. It feels right in theory, but in practice something is a little off.
My partner and coding buddy Orion pointed out to me recently that map is actually a simpler solution in most cases. The trick is knowing the right Enumerable methods to go with it. Like sum, perfect for the example above:
items.map { |item| item.price * item.quantity }.sum
This breaks the process into two steps: extracting the information you want, then operating on the result set. (I daresay this might be a simple version of map/reduce.) Plus, it works elegantly for hashes, something that always frustrates me about inject. For example, turning a hash into a key-value string with inject is:
hash.inject("") do |string, key_value|
string += "#{key_value[0]}=#{key_value[1]}\n"
end
With map, this is:
params.map { |key, value| "#{key}=#{value}" }.join("\n")
The two-step process wins out on elegance here, too.