Greetings adventurer, Let’s take look Iterators in Ruby.
What Is a Iterator?
Iterators in Ruby are several methods that can be used on a collection of data of data (such as arrays or hashes), which can be run through a block of code to perform tasks such as having each value or object in an array put out its name to the terminal. Iterators are endlessly powerful and allow us to not have to repeat ourselves. Let’s looks at what a simple version looks like in code:
On line 1 we have our array which is made up of video game mascots. Let us imagine that they are all on the same team. This team represents our array. On line 3 we see array_team followed by .each. What this is saying is to have each member of our team go through our block of code. Pretend that .each is a person giving our team a quest to enter a dungeon and our block of code is the dungeon. The entrance to our dungeon is represented by do and the exit is end. Each character enters the dungeon one at time represented by |character|, our argument for our iteration. When each character is in the dungeon, wherever it reads “character” imagine that it says each individual character’s name. Line 4 is the only command in the block but let’s imagine this line of code is a boss in a boss rush. The output for our code looks like this:
As each character went through the dungeon, the boss said the characters name in the terminal. The terminal then spits out the return value of the array, which is just the original array. That’s all fine and dandy, but it’s not much of a boss rush if there is only one boss in the dungeon. Time to complicate things a little to show off the power of our iteration.
Now the dungeon has more bosses and a forking path based on the condition of which character enters the dungeon. So now when Mario and Sonic enter the dungeon they look at the if statement on line 4 and ignore it because their name does not .include? the string “Man”. They then skip down to line 8 to fight the bosses there. When Mega Man and Pac Man see .include? they enter the if section of the dungeon, have a moral crisis and they leave the dungeon. This is because on line 5 .include? is asking the character if “Man” is included in their name. The output of this code looks like this:
What is is illustrated here is that each character follows an order of operations based on the code that applies to each character on our team. We see Mario and Sonic Fight all three bosses and get congratulated for completing the dungeon. We also see that Pac Man and Mega Man only enter the one room that applies to them and then complete their run through the block. Like the 1st example, the terminal spits out the original value of the array. What if we want the return value of our code to be a new array? This is where .collect comes in.
Each Vs Collect
.each aka .map executes the given block for each element of the array, then returns the array itself. This is illustrated in the examples above.
.collect also executes the given block for each element of the array, but returns a new array whose values are the return values of each iteration of the block.
Ruby likes to use multiple syntaxes to do the same methods so we can use .map and .collect interchangeably. The source code for the method .collect and .map uses .each inside of it to iterate — like most iterator methods. Time to see it in code.
Keeping with our familiar array_team, we will send our team to a similar block of code that we saw in our first example. This code outputs:
Since we called .collect, we are given a brand new array at the conclusion of our block. In this case we are now seeing the Boolean values for each character because of line 5’s .include? check for “Man”. If we would have excluded line 5 from our code we still would have been given a new array of our original array, but this allows us to see the visual difference. The reason we use .collect is so we can use that new array in other places, but that is a subject for another time.
There you are, adventurer: a simplified look at ruby iterators. Remember,