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
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
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
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:
Similarly, there are also
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
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.
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
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
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:
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 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