if, else & Loops
So far your code has run from top to bottom, every line once. Control flow lets a program make decisions ("if the marks are 75 or more, print Distinction") and repeat work ("for each city, print the temperature"). R has the same tools as other languages: if, else, for, while and repeat.
In this lesson you will write conditions and loops, learn the errors R gives when a condition is a whole vector, and see when a vectorised function is the better choice.
if, else if and else
if runs a block of code only when its condition is TRUE. Add else if for more cases and else for "everything else". R checks the conditions from top to bottom and runs the first block that matches:
marks <- 72
if (marks >= 75) {
grade <- "Distinction"
} else if (marks >= 60) {
grade <- "First division"
} else if (marks >= 40) {
grade <- "Pass"
} else {
grade <- "Fail"
}
grade[1] "First division"
The condition goes in round brackets and the code in curly braces. 72 is not 75 or more, but it is 60 or more, so the second block runs and R skips the rest. Change marks to 35 or 90 and run it again.
One style rule matters in R: put else on the same line as the closing brace }. If else starts a new line at the top level of a script, R thinks the if has already finished and reports an "unexpected 'else'" error.
switch() for many fixed choices
When you compare one value against many fixed options, switch() is shorter than a chain of else if:
day <- "Sunday"
switch(day, Saturday = , Sunday = "Weekend", "Weekday")
day <- "Tuesday"
switch(day, Saturday = , Sunday = "Weekend", "Weekday")
temps <- c(31, 38)
if (temps > 35) print("Hot")[1] "Weekend"
[1] "Weekday"
Error in if (temps > 35) print("Hot") : the condition has length > 1switch()looks for an argument whose name matchesdayand returns its value.Saturday = ,with nothing after the="falls through" to the next value, so Saturday and Sunday share "Weekend".- The last, unnamed value is the default for everything else.
The last line shows a classic error. if needs one TRUE or FALSE, but temps > 35 gives two values. Since R 4.2.0 this is an error (older versions only warned and used the first value, hiding bugs). To act on each element, use the vectorised ifelse() from Vectorised Arithmetic. To ask about the whole vector, use any(temps > 35) or all(temps > 35).
for loops
A for loop runs its block once for each element of a vector:
cities <- c("Rohtak", "Jaipur", "Pune")
temps <- c(33, 36, 29)
for (i in seq_along(cities)) {
cat(cities[i], "is", temps[i], "degrees\n")
}
total <- 0
for (t in temps) {
total <- total + t
}
totalRohtak is 33 degrees Jaipur is 36 degrees Pune is 29 degrees [1] 98
seq_along(cities)gives the positions 1, 2, 3, soican index two vectors in step.- The second loop walks over the values directly:
tis 33, then 36, then 29. - Values are not printed automatically inside a loop, so the first loop uses
cat().
(The second loop only shows the pattern; in real code sum(temps) does the same job in one step.)
while, repeat, break and next
A while loop repeats as long as its condition stays TRUE. Use it when you do not know in advance how many steps you need. Priya has ₹10,000 saved and adds ₹1,500 each month; how many months until she reaches ₹15,000?
savings <- 10000
months <- 0
while (savings < 15000) {
savings <- savings + 1500
months <- months + 1
}
months
savings
for (n in 1:10) {
if (n %% 2 == 0) next
if (n > 7) break
print(n)
}[1] 4 [1] 16000 [1] 1 [1] 3 [1] 5 [1] 7
After 4 months she has ₹16,000, the first amount that is not below the target. Make sure something inside a while loop changes the condition; otherwise the loop never ends (press Esc in your IDE to stop it).
The second loop shows two keywords that work in every kind of loop:
nextskips the rest of this step and moves to the next one. Here it skips even numbers.breakleaves the loop completely. Here it stops oncenpasses 7.
R also has repeat { ... }, a loop with no condition that runs until a break. It is rare; a while loop is usually clearer.
Two loop habits that prevent bugs
x <- c()
for (i in 1:length(x)) print(i)
for (i in seq_along(x)) print(i)
squares <- numeric(5)
for (i in 1:5) squares[i] <- i^2
squares[1] 1 [1] 0 [1] 1 4 9 16 25
Use seq_along(x), not 1:length(x). When x is empty, length(x) is 0 and 1:0 counts down: 1, 0. The first loop runs twice when it should not run at all. seq_along(x) gives an empty sequence, so the second loop correctly does nothing.
Create the result vector first. numeric(5) makes a vector of five zeros, and the loop fills it in. Growing a vector one element at a time with c() inside a long loop is much slower, because R copies the whole vector at every step.
Common mistakes
- Passing a vector to
if. "the condition has length > 1" means you needifelse(),any()orall(). - Putting
elseon a new line at the top level of a script. Keep} else {together. 1:length(x)on empty vectors. Useseq_along(x).- Infinite
whileloops. Make sure the loop changes the variable in its condition. - Writing a loop when a vectorised function exists.
sum(),cumsum(),ifelse()and friends are shorter and faster.
Key points
if,else ifandelsechoose which block runs; the condition must be a singleTRUEorFALSE.switch()picks a value from fixed named options, with an unnamed default.forloops over a vector; useseq_along()for positions.whileloops until its condition isFALSE.nextskips to the next step andbreakleaves the loop.- Pre-allocate results with
numeric(n)and prefer vectorised code where possible.
Exercise
Write a loop that goes through these monthly electricity bills of a family in Kochi: 1450, 1720, 2980, 3310, 2650 and 1580 (in rupees). For each month print "Month 1: normal" if the bill is under ₹2,500 and "Month 1: high" otherwise. After the loop, print how many months were high. Then do the labelling again in one line with ifelse().
Show solution
bills <- c(1450, 1720, 2980, 3310, 2650, 1580)
high_count <- 0
for (i in seq_along(bills)) {
if (bills[i] < 2500) {
cat("Month", i, ": normal\n")
} else {
cat("Month", i, ": high\n")
high_count <- high_count + 1
}
}
high_count
ifelse(bills < 2500, "normal", "high")Month 1 : normal Month 2 : normal Month 3 : high Month 4 : high Month 5 : high Month 6 : normal [1] 3 [1] "normal" "normal" "high" "high" "high" "normal"
The loop needs a counter and a cat() call, while ifelse() produces all six labels at once. cat() puts a space between its arguments, which is why there is a space before each colon; paste0() or sprintf() from Strings & Regular Expressions give you full control.
Quick Quiz
1. What happens with if (c(TRUE, FALSE)) print("yes") in R 4.6?
Since R 4.2.0, if requires a condition of length 1.
2. Which keyword skips to the next step of a loop?
R uses next; continue is the word in many other languages.
3. Why is seq_along(x) safer than 1:length(x)?
1:0 gives 1, 0, so a loop over an empty vector would run twice.
4. In switch(x, a = , b = "AB", "other"), what is returned when x is "a"?
An empty argument falls through to the next value, so a and b both return "AB".
Learn R Programming with a mentor
Free lessons get you started. Our Data Science & Machine Learning training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
