«

»

Aug 13

The trouble with injection

Ruby’s injection is very useful, but if you don’t remember one key fact, you’ll shoot yourself in the foot.

The inject method allows you to perform an operation over all the members of an Enumerable, keeping track of a value throughout.  However, the caveat is that you must return the value at each step.
Suppose we wanted to obtain the sum of the numbers from 1 to 100?

Ok, that works. But what about the sum of the even numbers?

That’s not at all what we expected. Here’s why:

If you recall from before, the value needs to be returned at each pass through the block.  However, here if the number is not even, the block returns an implicit nil.  Then things get weird when we attempt to add a number to nil.  Let’s try this again:

Much better. (edited per comment below by Jeremy Henty)

Another place where errors occur are with arrays and hashes.  Suppose I wanted (contrived) to collect objects representing the next seven days in an array.  Here’s an attempt (which fails):

What’s happening can be illustrated below:

The assignment returns the value assigned. Since it is not an array, when we attempt to use an index, we get an error. Here’s a version which works:

Note the use of Array#push which returns the entire array. Admittedly, it’s very contrived. A better way would be to use map, but that’s a discussion for another day.

2 comments

  1. Jeremy Henty

    “=> 2551 Much better.” D’ya think? What are the chances of
    the sum of even numbers being odd? How about

    irb(main):002:0> (1 .. 100).inject(0) {|sum, n| (n % 2)==0 ? sum + n : sum }
    => 2550

    That’s better!

  2. Matt Williams

    D’oh! Yeah, you’re right; without the 0 as the argument to inject, it uses the first element as the seed.

Leave a Reply

%d bloggers like this: