When I first found the inject method, it became a new favorite tool.

It was great to be able to turn something like this:


a = [7,8,9]
b = [1,2,3,4]
b.each { |e| a << e + 1}

into something like this:


b = [1,2,3,4]
a = b.inject([7,8,9]) { |s,e| s << e + 1 }

It was cool because it was even possible to build hashes this way, if you used a little trick.


b = [1,2,3,4]
a = b.inject({}) { |s,e| s[e.to_s] = e; s }

This should have given me the red flag though. Why should I need to pass the hash as the last value in the block?

go ahead, try something like this:

b = [1,2,3,4]
a = b.inject([]) { |s,e| s << e if e < 3 }

Now what I would *want* this to do is give me something like a #=> [1,2], but instead it gives us a #=> nil

So we have to do that annoying trick again:

b = [1,2,3,4]
a = b.inject([]) { |s,e| s << e if e < 3; s }

Now, I'm no nuby, but I suppose there is a little bit of nubyism in all of us. I just learned last week why inject was surprising me so much. It's not really just a shortcut for each that gives you a base value to build up, it's a functional method.

For those who aren't familiar with functional programming, there is alway the wikipedia, but the relevant part for this particular problem is that inject isn't designed to do anything destructive. That means things like << and += are bad and things like + are good.

That’s why you see the typical sum example of inject using just a + operator


sum = [1,2,3,4].inject(0) { |s,e| s + e } #=> 10

And if you want to build a hash, you can do so without the hack I showed before

hash = [1,2,3,4].inject({}) { |s,e| s.merge( { e.to_s => e } ) }

The reason these bits of code work is because the return value of the block is what becomes the new s, NOT the original parameter you passed to inject.

Now you might say ‘hey, this is probably pretty slow, building all these new references and passing them around’. I sort of thought the same thing my self, and am not quite sure how I feel about that.

But the idea is, you’re really fighting the function when you use destructive methods. If you find yourself needing to modify the original object rather than build a result set functionally, each isn’t THAT ugly. :)