Let’s talk about for loops.

Sometimes, you want to cycle through your data and do stuff do it, one-at-a-time. R has a lot of methods for doing so.

One method is using the apply family of functions, and this is generally seen to be a preferred method for many applications.

However, the point of this session is to teach a for loop, so we will not use those (but maybe sometime in the future?)

The basic logic of a for loop is:

1. for every something in some range
2. do something until that range is exhausted

Create a simple for loop

The way to write a for loop is similar to the if and else statements we used earlier in the session on function building, including our friends the brackets. The basic syntax is:

for(elements in range){
  do something
}

For example, we could ask R to print out each element of a vector. We will use i to represent the items that are being looped through. We could use any arbitrary value because it represents a variable that will stand for each element inside of a loop.

# define a vector
v1 = c('v', 'u', 'w')

for(i in v1){
  print(i)
}
## [1] "v"
## [1] "u"
## [1] "w"

Task 01

Can you write a for loop that prints out the numbers 0-10 in reverse order?

for(i in seq(10, 1, -1)) {
  print(i)
}
## [1] 10
## [1] 9
## [1] 8
## [1] 7
## [1] 6
## [1] 5
## [1] 4
## [1] 3
## [1] 2
## [1] 1

We can do more than printing within a for loop. For example, this loop performs a mathematical operation on each item:

for(i in seq(10000, 1000, -1000)){
  i <- -1000/i
  print(i)
}
## [1] -0.1
## [1] -0.1111111
## [1] -0.125
## [1] -0.1428571
## [1] -0.1666667
## [1] -0.2
## [1] -0.25
## [1] -0.3333333
## [1] -0.5
## [1] -1

Task 02

Can you write a for loop which doubles the values between 1 and 10?

for(number in 1:10){
  number = number*2
  print(number)
  }
## [1] 2
## [1] 4
## [1] 6
## [1] 8
## [1] 10
## [1] 12
## [1] 14
## [1] 16
## [1] 18
## [1] 20

You should now understand the basics of performing an explicitly defined for loop. Let’s add a layer of complexity and add a conditional statement inside of our for loop using an if and else statement. You do this exactly the same way that we learned inside of user-defined functions. For example, this loop prints every number greater than 2 from a vector of numbers:

for(i in c(1,2.5,1.2, 2.3, 5)){
  if(i > 2){
    print(i)
  }
}
## [1] 2.5
## [1] 2.3
## [1] 5

You can then add an else statement to do something when the first if does not trigger:

for(i in c(1,2.5,1.2, 2.3, 5, 2)){
  if(i > 2){
    print(i)
  } else {
    message(i, ' is not larger than 2!')
  }
}
## 1 is not larger than 2!
## [1] 2.5
## 1.2 is not larger than 2!
## [1] 2.3
## [1] 5
## 2 is not larger than 2!

Task 03

Create a variable named countries which contains the strings `New Zealand’, ‘Australia’, and the three countries which are part of North America. Type out the full name of each country - do not use acronyms.

Then write a for loop which checks whether the name of the country is longer than 6 letters. If it is, print the name of the country and a short message about that country. If the length is less than 6, print the name of the country and the phrase ‘I guess your name is only six letters long.’

You will have to use length(unlist(strsplit(word, ''))) in order to turn each word into letters and check the length.

countries <- c('New Zealand', 'Australia', 'United States of America', 'Canada', 'Mexico')

country_output <- function(){
for(c in countries){
  if(length(unlist(strsplit(c, ''))) > 6){
    message('The country ', c, ' seems to have a long name!' )
  } else {
    print(paste(c, "I guess your name is only six letters long or less."))
    }
  }
}

Your output should look something like this:

country_output()
## The country New Zealand seems to have a long name!
## The country Australia seems to have a long name!
## The country United States of America seems to have a long name!
## [1] "Canada I guess your name is only six letters long or less."
## [1] "Mexico I guess your name is only six letters long or less."

We can make loops even more intelligent by storing information in a loop as it processes. This information can be checked against future loops and decisions can be made along the way. Importantly, any temporary variable stored within a loop will be reset each time the loop starts. For example, compare these two loops:

# x is set to zero at the start of each loop
for(i in 1:10){
  x = 0
  x = x + 1
  print(x)
}
## [1] 1
## [1] 1
## [1] 1
## [1] 1
## [1] 1
## [1] 1
## [1] 1
## [1] 1
## [1] 1
## [1] 1

Now look what happens when we set to 0 outside of the for loop

# x is defined outside of the for loop
x <- 0

for(i in 1:10){
  x = x + 1
  print(x)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10

As such it’s pretty important to properly scope your variables for a loop - otherwise you might accidentally reset or not reset an important variable each time, which could have negative consequences if you are checking the variable for a reason .

Compare these two for loops - which one is better?

# temp variable stored outside a loop

winner = 0
  
for(i in c(1,4,6,3,8,2,2,4,100)){

  if(i > 3 & i > winner){
    winner = i
  }
  message('the current winner is ', winner)
}
## the current winner is 0
## the current winner is 4
## the current winner is 6
## the current winner is 6
## the current winner is 8
## the current winner is 8
## the current winner is 8
## the current winner is 8
## the current winner is 100
# temp variable stored inside a loop
for(i in c(1,4,6,3,8,2,2,4,100)){
  winner = 0

  if(i > 3 & i > winner){
    winner = i
  }
  message('the current winner is ', winner)
}
## the current winner is 0
## the current winner is 4
## the current winner is 6
## the current winner is 0
## the current winner is 8
## the current winner is 0
## the current winner is 0
## the current winner is 4
## the current winner is 100

At this point we want to start storing our for loops inside functions so we can control the scope of variables used for any particular loop. If we only keep a variable inside a function, it will not enter our global environment (and thus not cause later issues. )

loopy <- function(){
  
  # this temporary variable is inside a function but not inside the for loop. 
  output = ''
  
  for(i in unlist(strsplit('Hello World', ''))){
    output <- i
    print(output)
  }
}

# run the function
loopy()
## [1] "H"
## [1] "e"
## [1] "l"
## [1] "l"
## [1] "o"
## [1] " "
## [1] "W"
## [1] "o"
## [1] "r"
## [1] "l"
## [1] "d"

What happens when I try to call output from outside of the function?

# output
# Error: object 'output' not found

Task 04

Create a function named long_name_checker which takes a single argument names.

Inside the function, check whether each name in a list is longer than 5 letters. If so, it’s a long name. If it’s longer than 8 letters, it’s a super long name. Otherwise it’s a short name. Create some appropriate output messages for each condition.

Test it with these names: names = c('', 'Jerry', 'Elaine', 'Kramer', 'George', 'Seinfeld', 'Fraser', 'Rumplestiltskin')

long_name_checker <- function(list_of_names){
  for(i in list_of_names){
    name_length = length(unlist(strsplit(i, '')))
    if(name_length >= 8){
     message(i, 'is a super long name!')
    } else if(name_length > 5 & name_length < 8){
     message(i, ' your name is long')
    } else {
     message()
    }
  }
}


names = c('Jerry', 'Elaine', 'Kramer', 'George', 'Seinfeld', 'Fraser', 'Rumplestiltskin')

long_name_checker(names)
## 
## Elaine your name is long
## Kramer your name is long
## George your name is long
## Seinfeldis a super long name!
## Fraser your name is long
## Rumplestiltskinis a super long name!

Task 05

Make a new function named long_name_checker_02 which is a modified version of your function from Task 04 so that it now stores all names that count as super long (which should be two names). At the end of your first for loop, add another for loop which then tells you which of the super long names are the longest. Use a message to output the correct results.

Store the names in an empty variable named super_long_names - you can initialize it with super_long_names <- c(). You must store this variable inside the function - so think carefully about where you place it. You will also need to store a temporary variable to check which of the super long names is the longest - think carefully where this one goes too!

long_name_checker_02 <- function(list_of_names){
  super_long_names = c()
  
  for(i in list_of_names){
    name_length = length(unlist(strsplit(i, '')))
    if(name_length >= 8){
      message(i, ' is a super long name!')
      super_long_names <- c(super_long_names, i)
    } else if(name_length > 5 & name_length < 8){
      message(i, ' your name is long')
    } else {
      message(i, ' i guess you have a short name. ')
    }
  }
  
  for(s in super_long_names){
    longest_name = ''
    super_long_length = length(unlist(strsplit(s, '')))
    if(super_long_length > length(unlist(strsplit(longest_name, '')))){
      longest_name = s
    }
  }
  message(longest_name, ' CONGRATULATIONS, you have the longest name.')
}

names = c('Jerry', 'Elaine', 'Kramer', 'George', 'Seinfeld', 'Fraser', 'Rumplestiltskin')

long_name_checker_02(names)
## Jerry i guess you have a short name.
## Elaine your name is long
## Kramer your name is long
## George your name is long
## Seinfeld is a super long name!
## Fraser your name is long
## Rumplestiltskin is a super long name!
## Rumplestiltskin CONGRATULATIONS, you have the longest name.

Task 06

Define a function named print_letters that takes no arguments.

This function will contain a for loop which checks each letter of the alphabet. If the letter is a vowel, the loop should print the vowel and the order of the vowel in the alphabet (e.g., ‘A’ is the first vowel, ‘E’ is the second vowel - so your output for vowels should range from 1-5 - don’t include ‘Y’). If the letter is a consonant, print the numerical position of that letter in the alphabet (e.g., ‘B’ is 2, ‘Z’ = 26.)

Tips:

print_letters <- function(){
  counter.all <- 0
  counter.vowel <- 0

for(l in letters){
  
  if(l %in% c('a','e','i', 'o', 'u')){
    
    counter.vowel = counter.vowel + 1
    counter.all = counter.all + 1
    print(paste(l, counter.vowel))
  
    } else {
    
    counter.all = counter.all +1
    print(paste(l, counter.all))
    }
  }
}

Successful output looks like this:

print_letters()
## [1] "a 1"
## [1] "b 2"
## [1] "c 3"
## [1] "d 4"
## [1] "e 2"
## [1] "f 6"
## [1] "g 7"
## [1] "h 8"
## [1] "i 3"
## [1] "j 10"
## [1] "k 11"
## [1] "l 12"
## [1] "m 13"
## [1] "n 14"
## [1] "o 4"
## [1] "p 16"
## [1] "q 17"
## [1] "r 18"
## [1] "s 19"
## [1] "t 20"
## [1] "u 5"
## [1] "v 22"
## [1] "w 23"
## [1] "x 24"
## [1] "y 25"
## [1] "z 26"