Ruby: Loops and Iterators
Loops are structures in Ruby which allow you to easily repeat a section of code a number of times. This functionality can always be accomplished through simply writing repetitive lines of code, however compacting everything into one loop makes changing a small feature about each repeated line very easy, and it abides to the core "Don't Repeat Yourself" principle of programming.
While and Until
The first two loops which we're going to learn about are the "while" and "until" loops. These repeat a section of code while a condition is true, or until a condition is true (as appropriate). They are created by writing the while
or until
keywords, the condition, the do
keyword or a new line to separate the condition and the next section, the code to repeat, and then the end
keyword to end the structure. Essentially they work much like 'if' statements, we have the main keyword, a condition, something to separate the condition and the rest of the code, the code we want to be a part of this structure, and then the end
keyword to end the structure.
The "while" and "until" loops are essentially just inverted versions of each other. "While" repeats the code while the condition is true, and "until" loops while the condition is false (in other words, until the condition is true). The most basic "while" and "until" loops which just loop infinitely as they are given straight-up boolean conditions can be seen as follows:
1 2 3 4 5 6 7 8 |
|
In the case of the above infinite loops, we can break out of the loop in a certain situation by using the break
keyword (often used in conjunction with 'if' statements to break out of infinite loops in certain conditions) - however it's much better practise in most situations to make use of the actual while
conditions. While loops in general are intended to be used with true
or false
conditions, such as variables or method calls. We can, however, also use them to iterate (step) through a sequence of numbers by counting each repeat using a variable and then using this variable in the loop's condition. Take the following, for example, in which the code inside the loop is executed five times, counting from 1 to 5:
1 2 3 4 5 6 |
|
Both keywords, while
and until
, can also be used as modifiers. Modifiers in Ruby mean that whatever expression the modifier is applied to will only be evaluated by the Ruby interpreter if the modifier says so. Since everything in Ruby is an expression, which means everything in Ruby has a value of some kind, modifiers can be used on pretty much everything in Ruby! The modifiers for "while" and "until" are simply represented by tagging their keywords plus a condition to an expression - take, for example, the following one line loop:
1
|
|
Similarly, there are also if
and unless
modifiers that can apply basic conditional functionality (as seen in 'if' statements) to expressions. We haven't actually talked about "unless" before, but essentially just like "until" is an invert of "while", "unless" is an invert of "if". Take, for example, the following (in which unless
is used as a modifier):
1 2 3 |
|
Moving from this, these modifiers can also be applied to blocks. Blocks, in Ruby, are simply sections of grouped code which are treated as a single expression or value. This means that they can be passed to supporting methods, or in this case, can be used with modifiers.
Blocks are created in Ruby by either using curly brackets, or by using the do
and end
keywords. There is much debate about which form should be used where, but for most purposes it doesn't really matter. Our 'until' loop which counted from one to five earlier could be re-created using a block and the while
modifier (just to mix things up in this case), as follows:
1 2 3 4 5 6 |
|
In the above case, using the modifier in this sense is simply another way to solve the "problem" of easily counting to five. This is a classic good use-case of loops as the numbers one to five could easily be outputted by simply using five puts
calls, however with this method the number of times to loop can be easily changed, and certain mathematical operators or other things could be applied to every number the loop counts through.
For
The "for" loop in Ruby is a little bit different to the "while" and "until" loops. If you've programmed in any other languages before, the "for" loop in Ruby is also a bit different to the "for" loop in most other programming languages. The "for" loop is essentially for looping over the values in an array or hash. It's written via the for
keyword, followed by a variable name (or multiple, we'll come back to this), followed by the in
keyword, followed by an array or hash to loop through, followed by the do
keyword or a new line, followed by the code to repeat, followed by the end
keyword.
This probably sounds a little bit confusing, but essentially the variable name written before the in
keyword will be used to store each entry in the array or hash specified after the in
keyword, and this can be utilized inside the loop. Take, for example, the following, which outputs all the elements in the array:
1 2 3 4 5 |
|
In this case "employee" is just the name we've given to a variable which is going to hold the relevant employee at each repeat of the loop - the loop will end up looping as many times as there are elements in the array.
If this type of functionality is used with a hash, it will loop through all the keys and all the values. This often isn't the intended functionality, and as such, two variables can be specified (separated by a comma) to the loop so that the keys and values can be treated separately. This functionality is demonstrated in the following code snippet:
1 2 3 4 5 |
|
The "for" loop can also be used to easily iterate through simple number sequences, just as we did with while
and until
earlier. The first variable in the loop structure will hold the number at each "step" of the loop, and for the "array" we can specify a pattern by using ..
- for example 1..5
specifies the numbers one to five. This makes looping a certain number of times much easier than using "while" or "until", as can be seen in the following code snippet:
1 2 3 |
|
Times and Each
The last two (or three) "loops" we're going to cover in this tutorial (we're not covering all of the possible methods of repeating code, there isn't enough time in the day!) aren't actually "loop structures" per-se, but are actually just methods which take blocks (as discussed earlier, sections of code denoted by curly brackets or "do/end"). These are called iterators.
The first method we're going to talk about, times
, can be performed on any number object and simply loops the given block that number of times. The following, for example, outputs "Hi" five times:
1
|
|
Sometimes blocks also have values available for them to use, and when this is the case they can be assigned to variables through the use of a comma separated list of variables surrounded in pipe symbols at the beginning of the block. In this case, the times
method actually lets us see the value of the variable it's using to count each loop step, and so we can assign this to a variable and make use of it:
1 2 3 |
|
The above will count from zero to four as the variable which counts the loop steps, which we're putting in 'i', in this case starts at zero and increases by one each time.
The final two methods we're going to talk about are each
and each_index
. Using each
on an array or hash is essentially identical to using a for loop - the block specified repeats the same number of times as elements or keys and values in the array or hash, and the values for the array elements or hash key/values can be utilized as they are passed to the block. For example:
1 2 3 4 5 |
|
Using this over for
is literally just a matter of syntax and what you prefer to write, but that's one of the reasons that we all love Ruby - it's extremely flexible and there are a bunch of different ways to accomplish the same goal.
Finally the last method we're going to talk about, each_index
, loops the same number of times as there are elements in an array, but instead of providing the values of each element directly, it provides the various index numbers. This can be useful for performance in situations where the value of each element shouldn't always be used, and something like array[i]
can get the value at the given index if/when it does need to be used. In this case, we can just utilize each_index
to count up the number of elements in an array:
1 2 3 4 5 |
|