Concept | Description |
---|---|
Iteration | To traverse or visit every element in an Array object |
each | The each method can be called on an Array object to iterate over the elements it contains |
each_with_index | Allows for an additional variable to be used in the pipe operators that will represent the index of the current element |
map | Can be used to iterate over every element in an Array while applying a method to each instance |
Loop constructs | A loop construct allows for iteration of an object for a given number of times or while a condition is met |
Objectives
- Understand the concept of iteration.
- Apply the each method to iterate over an Array.
Loop: Loops allow a programmer to run a block of code a known or unknown number of times. They allow us to write less code. .
The each method allows us to loop through objects of the Array and Hash classes.
toy_chest = [
"TMNT Figurines",
"Tonka Truck",
"Fort Legoredo",
"Baseball",
"Baseball Glove",
"Motherboard"
]
toy_chest.each do |toy|
p "I remember my #{toy}!"
end
-
toy
is a stand-in for every element in the collection we are iterating over.- It's critical to use good variable names that represent what we are dealing with in order to improve the readability of the code.
-
next
Method:- Imagine that we are emptying out our toy_chest to give the toys away for charity. We really love our Fort Legoredo Lego set and don't want to give it up.
- We can use the
next
method to skip that toy.
toy_chest = [
"TMNT Figurines",
"Tonka Truck",
"Fort Legoredo",
"Baseball",
"Baseball Glove",
"Motherboard"
]
toy_chest.each do |toy|
if toy == "Fort Legoredo"
next
else
p "Removing #{toy}"
end
end
# could also be written as follows:
toy_chest.each do |toy|
next if toy == "Fort Legoredo" #inline next
p "Removing #{toy}"
end
=begin
both return:
Removing TMNT Figurines
Removing Tonka Truck
Removing Baseball
Removing Baseball Glove
Removing Motherboard
=end
break
Statement:- Allows us to break out of the loop and escape the block.
- Let's do a quick search for an umbrella inside our closet.
- We want to search the closet and as soon as we find the umbrella, we want to stop searching.
closet = [
"Red hat",
"White hat",
"Black hat",
"Umbrella",
"Running shoes",
"Red clown shoes"
]
closet.each do |item|
p "Grabbing the #{item}"
if item == "Umbrella"
break
else
p "Not the #{item}"
end
end
# could also be written as follows:
closet.each do |item|
p "Grabbing the #{item}"
break if item == "Umbrella" #inline break
p "Not the #{item}"
end
=begin
both return:
Grabbing the Red hat
Not the Red hat
Grabbing the White hat
Not the White hat
Grabbing the Black hat
Not the Black hat
Grabbing the Umbrella
=end
- Note: like the return statement, nothing below a break or next statement will be executed.
_Sometimes it's useful to know what position an element occupies in the Array. each_with_index
allows us find o that information. _
- Instead of one argument in, it takes two.
- The first argument will be the current element.
- The second argument will be the index.
numbers = [1,2,3]
numbers.each_with_index do |number, index|
p "element #{number} is at index #{index}"
end
=begin
"element 1 is at index 0"
"element 2 is at index 1"
"element 3 is at index 2"
=end
If we want to transform the elements in the collection, we can use the map method.
bag = ["popcorn", "frozen pizza", "coffee", "donuts", 5]
bag.map do |item|
next if item.is_a? Integer
"Gluten-free #{item}"
end
-
In the case above, we are skipping the current iteration if the item is an integer.
-
The map method will iterate over all the elements in the bag and add the words "Gluten-free" before each item.
-
The program will return a new array with the gluten-free items but the bag collection remains unchanged.
-
map!
- We can
map
it in place by using the mutator version of map calledmap!
. - At first, map! might seem more complicated, but it’s really a simpler way to do an each to produce an Array.
- For instance, the code above can be written like this:
- We can
bag = ["popcorn", "frozen pizza", "coffee", "donuts", 5]
result = []
bag.each do |item|
next if item.is_a? Integer
result << "Gluten-free #{item}"
end
# result is now the array of strings with a prefix of “Gluten-free”
The for loop construct works almost exactly like the each loop. The main difference is regarding scope. The variable for a for loop must be outside the scope of the call, while the variable of an each loop (the variable between the pipes) is thrown away after the block executes.
- Let's take a look at the format used in a for loop:
#using the reserved word do
for variable in collection do
#execute this block for each
end
#using a newline
for variable in collection
#execute this block for each
end
#using a semicolon
for variable in collection ;
#execute this block for each
end
- A for loop begins with the reserved word
for
. - It's followed by a variable name that will be our stand-in for each element we'll be visiting in our loop.
in
is another reserved word. It tells our for loop what object we want to iterate over.- That keyword is followed by the collection we want to iterate over.
- Lastly, on that same line, an optional
do
or;
tells our loop that the loop body will begin after it.- Inside the loop, our code will execute for each item in the collection.
- The loop is then closed with another reserved word,
end
.
Example: Let's take a look at some code that uses a for loop to iterate over an Array.
- If the current element is a umber, it will print the result of squaring that number.
- If the current element is a string, it will print "Can't square strings."
- For anything else, it will print "Don't know what to do with that."
potential_numbers = [1, 2, 3, "word", 4, true, Hash.new, 5, [4], "another word"]
for element in potential_numbers
if element.is_a? Numeric
p element ** 2
elsif element.is_a? String
p "Can't square strings."
else
p "Don't know what to do with that."
end
end
=begin
returns:
1
4
9
Can't square strings.
16
Don't know what to do with that.
Don't know what to do with that.
25
Don't know what to do with that.
Can't square strings.
=end
Exponenet operator: The **
operator is known as the Exponent operator. It raises the number on the left to the power on the right.
Sometimes we need the code to run until something changes. The while loop is a helpful construct for this.
Basic while loop formula:
while condition # while `condition` evaluates to **true**
# run this awesome block of code until the condition evaluates to false
end
# with the optional `do`
while condition do
# run this awesome block of code until the condition evaluates to false
end
# or optional backslash
while condition \
# run this awesome block of code until the condition evaluates to false
end
while condition ;
# run this awesome block of code until the condition evaluates to false
end
while
Loop Construction:
while
: The loop begins with the keyword while and is followed by condition. It must be boolean.do, backslash, semicolon or newline
: The condition is followed by an optional do, backslash, semicolon or newline that signals the end of the loop heading.- The loop body will run until the condition evaluates as false.
end
: The end keyword closes the loop body.
- The while loop has several good use cases, but it's important to note that it is also likely to result in an infinite loop.
- It must be possible for the condition to be made false inside the loop, otherwise our loop won't stop executing.
Let's write a loop that spends our money until we're broke:
$money = 5 #global variables start with $
while $money > 0
p "You still have #{$money} in the bank."
$money -= 1 # -= is the decrement operator. It reduces the value of the variable on the left by the number on the right
end
=begin
returns:
You still have 5 in the bank.
You still have 4 in the bank.
You still have 3 in the bank.
You still have 2 in the bank.
You still have 1 in the bank.
=end
- Global Scope Variables: The
$
sign as the first character in a variable name will make that variable a global variable. That means that the variable will be available in the global scope of the program. We need a global variable here because our loop creates it's own local scope it doesn’t know about the regular variables out of the local scope.
The until loop construct is the inverse of the while loop. It works the same way, except that the loop executes until the condition evaluates as true.
Let's take a look at the format for the until loop:
until condition # until `condition` evaluates to **true**
# run this awesome block of code until the condition evaluates to true
end
# with the optional `do`
until condition do
# run this awesome block of code until the condition evaluates to true
end
until condition ;
# run this awesome block of code until the condition evaluates to true
end
Rewrite the while
loop money example using an until
loop:
$money = 5 #global variables start with $
until $money < 1
p "You still have #{$money} in the bank."
$money -= 1
end
=begin
returns:
You still have 5 in the bank.
You still have 4 in the bank.
You still have 3 in the bank.
You still have 2 in the bank.
You still have 1 in the bank.
=end
We can use the times
method to have our loop execute a specific number of times.
times
is a method of the Integer class.- For this reason, it must be called on a number.
5.times do
p "This prints 5 times"
end
=begin
returns:
This prints 5 times
This prints 5 times
This prints 5 times
This prints 5 times
This prints 5 times
=end
If we need to know the number of the current iteration of the loop, we can pass a variable with the block:
5.times do |i|
p "Iteration #{i}"
end
=begin
returns:
Iteration 0
Iteration 1
Iteration 2
Iteration 3
Iteration 4
=end
The upto
method is another method of the Integer class.
- This method must be called on an number and called with an argument.
- There's another method called
downto
that does the opposite and counts down.
Here's the formula for it:
lower.upto(upper) do |iterator|
#body of loop
end
- In this case,
lower
refers to the starting point in the loop.- That means that our iterator will start at this number, instead of 0.
upper
will be the last number our iterator will execute on.
1.upto(10) do |num|
if num.even?
p "#{num} is an even number"
else
p "#{num} is an odd number"
end
end
=begin
returns:
1 is an odd number
2 is an even number
3 is an odd number
4 is an even number
5 is an odd number
6 is an even number
7 is an odd number
8 is an even number
9 is an odd number
10 is an even number
=end
- Nested Loops:
- Sometimes we need to work with multiple layers.
- Just as can nest conditional statements, we can also nest loops.
- This is helpful when doing table operations. The outer loop could represent a column and the inner loop would represent a row. We can represent tables using multi-dimensional Arrays (Arrays with Array elements).
5.times do |i| #outer loop
p "outer loop i: #{i}"
2.times do |j| #inner loop
p "inner loop j: #{j} for outer loop i: #{i}"
end
end
=begin
returns:
outer loop i: 0
inner loop j: 0 for outer loop i: 0
inner loop j: 1 for outer loop i: 0
outer loop i: 1
inner loop j: 0 for outer loop i: 1
inner loop j: 1 for outer loop i: 1
outer loop i: 2
inner loop j: 0 for outer loop i: 2
inner loop j: 1 for outer loop i: 2
outer loop i: 3
inner loop j: 0 for outer loop i: 3
inner loop j: 1 for outer loop i: 3
outer loop i: 4
inner loop j: 0 for outer loop i: 4
inner loop j: 1 for outer loop i: 4
=end
Note: Notice that we have to use a different iterator name for the inner loop. This is because our inner loop has access to the entire scope where i exists. Using the same variable name would cause it to overwrite the outer loop's iterator value.
Exercises - Title Case
Define a
Title
class which is initialized by a String.It has one method --fix-- that returns a title-cased version of the String:
We will need to use conditional logic --if and else statements -- to make this work. Read the test specification carefully so you understand the conditional logic to be implemented.
Some helpful methods to use are:
You can also use each_with_index to detect when you're on the first word, which should always be capitalized.