Ruby Inject Basics
This week during lecture at Flatiron school, Avi showed us Ruby’s .inject
method (also known as .reduce). I thought this seemed like a pretty cool and potentially powerful method and decided to do a bit more research so I could start using it with confidence in my code.
How Inject Works
.inject
is an iterator method (technically an enumerable method) that creates an accumulator variable that “accumulates” whatever you want as you iterate over some sort of collection and returns the accumulator when the iteration is complete.
Instead of trying to explain further let’s take a peek at a simple example.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Here we can see we’re iterating over the array and adding the element to the accumulator each time through. If you look at the output from the puts you can see that the accumulator starts at 1, the first element in the array, and the element variable is given the 2nd element of the array. As it iterates through the array, the sum becomes the accumulator’s value and then is returned at the end of the iteration.
Here’s that same example but this time we’re multiplying the elements in the array.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Giving the Accumulator a Starting Value
In the last two examples we’re not giving the accumulator a starting value, however we have the option to do this by passing the value to the .inject method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
This time we’re summing the contents of the array, like in the first example, but we’re giving the inject method a value to set the accumulator to. Here we’ve set it to 0, but you can set it to anything you want. You can see from the output that the accumulator starts at 0 and the element variable is set to the first element in the array, 1.
But why do we want to set the accumulator to 0? This is just making us loop through one additional time?
For this summing example, this is true but if we we’re doing something to the element on each iteration, like say incrementing it by two, we need to give the accumulator variable a starting value. If we don’t then it uses the first element in the array, we can’t increment it, and thus don’t get the return value we want.
Here’s a look at this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
You can see that if we don’t set the accumulator to 0 we don’t increment the first element by 2 and thus get 16 when we really wanted 18. Because of this, it’s probably a good idea to get in the habit of setting the accumulator value no matter what.
Altering Hashes
Another use case for .inject (.reduce) is if you need to change the key or value in a hash.
1 2 3 4 5 6 7 8 9 10 11 |
|
In this example, we have a hash_of_state_capitals
whose keys are strings, and we want to change them to symbols.
This time around, instead of setting the accumulator to a number we’re setting it to an empty hash ( hash_of_state_capitals.reduce({})
), which we’re calling “new_hash”. Then each time we loop through the hash_of_state_capitals we create the new_hash by setting the key to the state.to_sym
and the value to capital. Then at the bottom we return the new hash, which is a must, and will throw an error if you don’t.
This method of altering a hash is a bit cleaner than using .each
and can actually be done on a single line.
1 2 3 4 5 |
|
Building Arrays & Hashes
You can use inject to build arrays and hashes similar to how you use other methods such as .map or .select. The advantage that inject has over these is you can use inject instead of chaining several methods together. This isn’t a big deal if you’re chaining just two methods together to achieve whatever you’re doing but your code could get messy if you go beyond that.
Here is a great example from Jay Fields’ blog post on Inject demonstrating how to use .inject
to get all the integers of an array, that are even, as strings.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
An example of building a hash would be if you received an array with nested arrays, that you wanted to convert into a hash as key, value pairs.
1 2 3 4 5 6 7 8 9 10 |
|
Here we have student’s IDs and names given to us as an array of arrays, but we want to convert this to a hash. The inject method takes care of this in a nice compact way, without having to create empty variables outside our loop.
Finally here is an example from the anagram_detector lab we did today.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
You can see in my commented out code that you can do the same thing with the .select
method but I think inject is almost as elegant and gives us the ability to do additional things that .select
can’t. Such as, build a hash with the returned strings or manipulate the strings further.
That’s All Folks
And that’s all I’ve got on Ruby’s Inject for now. From what I’ve read it seems .inject
becomes even more powerful when you start using it with objects but that’s for another blog post. To learn more below are links to all the blog posts I referenced for writing this post and I suggest reading them if you want to learn more.