Passing variables to blocks when chaining collection methods

One of the nicest things when we use ruby as a programming language is how flexible and easy it is to work with collections. We have tons of methods and we can work with the data inside this collections almost any way we want. And because of this flexibility, it’s possible to chain different methods from the Enumerator class and Enumerable module passing only one block. But, when we start to chain lots of methods, it can get confusing how we should pass the variables we want to work with to a block. Take the code below for example:

We have the methods, now we want to use a block. But, how do we use it? How do we pass all the variables to the block when we use all these methods? I want to access, on each iteration, the key and its value, the empty array and the hash index. Do we pass the variables to the block like this: {|key, value, arr, idx|}?

When I had to use a structure like this, sometimes I got really confused about how to use them, so I decided to write this post to help other developers that had the same problems as I did.

The first thing we have to do to start writing code like this is to look at the API of these methods at rubydoc.org. We can see that all of the each methods (along with their similar with_index and with_object) expect a block that gets TWO variables: *args and, depending on the method, the array or hash index, or the object being passed as argument to each_with_object. And that’s not surprising because when we use these methods alone, that’s exactly what we have been doing:

But, what if we were not using an array, but a Hash and calling each_with_object with it? How do we use it? One common mistake I used to make was thinking that each and each_with_object would work the same way when passing variables to a block. So for example, when we use each with hashes, we usually do something like this:

Well, if each work this way, then each_with_object must be like the code below, right?

Actually the variables being passed is not what we (at least what I was used to) think they are. arr variable in this case will be nil, value is an empty array and key is an array containing the key and the value of this iteration. We have to remember, each_with_object is expecting only two variables in the block. If we use a third, like I did above, each_with_object will ignore it. Let’s correct it then, passing only two variables:

Now arr is pointing, at the beginning of the iteration, to what we are expecting, an empty array. But what about the iteration_data variable? Which value does it have? These values now will be passed as an array. So iteration data value, at the first iteration is [:name, “Douglas”]:

But, can’t we use the key and the value separately? Yes, we can. When using blocks, we can “break” the array into different variables when we use parentheses. So we can make the following call:

Let’s complicate a little bit and use the same call from the beginning of this post:

Now we are chaining each_with_object with with_index. In these situations, the order of the variables depends on the order of the chaining. In this case, with_index is being called from the Enumerator object returned from each_with_object call. So the block that we will pass will get two variables, being the last one the index of the Hash. We can begin our construction like this

where idx is the index of the Hash. What about the rest? We learned from the above that the first variable, if it’s a complex structure like hash, will be put inside an array. That’s what will happen here. We will have then an array that something points to, containing in the first position another array, having :name symbol and “Douglas” string as the values of the first and second positions. At the second position outer array, we will have yet another array being this the one passed in the each_with_object method. Let’s break with parentheses in two steps to make it clearer:

But we want to separate key and value to work with them in different variables. Ok, let’s do it:

Now we can use four different variables inside our block code.

So basically that’s how we can collection methods where we want to use three or more variables in a block. That’s something I used to scratch my head with and now I think I’m getting more comfortable with.