if, ifelse(), and switch() for choicesfor, while, and repeat for loopsNote
Basic familiarity with choices and loops is assumed.
if is the basic statement for a choiceSingle line format
Compound statement within {
if can be assignedTip
Only recommended with single line format; otherwise hard to read.
if without else can be combined with c() or paste() to create compact expressionsif without else invisibly returns NULL when FALSE.if should have a single TRUE or FALSE condition, other inputs generate errors#> Error in if ("x") 1: argument is not interpretable as logical
#> Error in if (logical()) 1: argument is of length zero
#> Error in if (NA) 1: missing value where TRUE/FALSE needed
#> Error in if (c(TRUE, FALSE)) 1: the condition has length > 1
ifelse() for vectorized conditions#> [1] "1" "2" "3" "4" "XXX" "6" "7" "8" "9" "XXX"
#> [1] "odd" "even" "odd" "even" "odd" "even" "odd" "even" "odd" "even"
Tip
Only use ifelse() if both results are of the same type; otherwise output type is hard to predict.
dplyr::case_when() for multiple condition-vector pairsswitch() is a special purpose equivalent to if that can be used to compact codeswitch() is a special purpose equivalent to if that can be used to compact codeTip
switch() should always throw an error, otherwise unmatched inputs will invisibly return NULL.switch() with character inputs. Numeric inputs are hard to read and have undesirable failure modes.Caution
Like if, switch() can only take a single condition, not vector conditions
= emptyfor loopNote
Convention uses short variables like i, j, or k for iterating vector indices
for will overwrite existing variables in the current environmentnext or break to terminate loops earlynext exits the current iteration, but continues the loopbreak exits the entire loopnext or break to terminate loops earlyTip
vector() function is helpful for preallocation
seq_along(x) instead of 1:length(x)1:length(x) causes unexpected failure for 0 length vectors: works with both increasing and decreasing sequencesseq_along(x) instead of 1:length(x)seq_along(x) and x[[i]]while or repeat when you don’t know the number of iterationswhile(condition) action: perfoms action while condition is TRUErepeat(action): repeats action forever (or until a break)for before while or repeatapply() or purrr::map() before forif works with scalars; ifelse() works with vectors.
y be if x is TRUE? What if x is FALSE? What if x is NA?When x is TRUE, y will be 3; when FALSE, y will be NULL; when NA the if statement will throw an error.
switch("x", x = , y = 2, z = 3) return?This switch() statement makes use of fall-through so it will return 2.
Read the documentation and write down the rules in your own words.
The arguments of ifelse() are named test, yes and no. In general, ifelse() returns the entry for yes when test is TRUE, the entry for no when test is FALSE and NA when test is NA.
In practice, test is first converted to logical and if the result is neither TRUE nor FALSE, then as.logical(test) is returned.
#> [1] "not empty"
#> [1] "empty"
if() expects a logical condition, but also accepts a numeric vector where 0 is treated as FALSE and all other numbers are treated as TRUE. Numerical missing values (including NaN) lead to an error in the same way that a logical missing, NA, does.
0 indices when using [<- and [x[1] generates an NA. NA is assigned to the empty length-1 list out[1]x[0] returns numeric(0). numeric(0) is assigned to out[0]. Assigning a 0-length vector to a 0-length subset doesn’t change the object.Setup
First Iteration
Second Iteration
Final Result
In this loop x takes on the values of the initial xs (1, 2 and 3), indicating that it is evaluated just once in the beginning of the loop, not after each iteration. (Otherwise, we would run into an infinite loop.)
In a for loop the index is updated in the beginning of each iteration. Therefore, reassigning the index symbol during one iteration doesn’t affect the following iterations. (Again, we would otherwise run into an infinite loop.)