The difference between switch
and if-else
is mainly a stylistic choice. In general, the language-independent (c++, c#) rule-of-thumb is to use switch
when there are roughly 3-5 or more conditions to improve code readability. That said, there are some differences between switch
and if
in R that make it more than branching thingie.
Character input
> Most often used
> Example case: small dictionary.
f <- function(x) switch(x, a = 100, b = 200, c = 300, 0)
g <- function(x) if(x == "a") 100 else if(x == "b") 200 else if(x == "c") 300 else 0
You can supply the Expr
argument as a regular strings, in backticks or as names, or empty value. An empty element is falling through, meaning that the next will be evaluated. If no match is found, the default is returned.
chr_switch <- function(expr) {
switch(
expr,
"a" = 1,
b =,
`c` = 3,
4
)
}
sapply(c("a", "b", "c", "d"), chr_switch)
#> a b c d
#> 1 3 3 4
Numeric input
> Not used as often
> Example case 1: if n arguments are given, do that expression
f <- function(...) switch(...length(), 100, 200, 300)
g <- function(...) {l = ...length(); if(l == 1) 100 else if(l == 2) 200 else 300}
num_switch <- function(expr) switch(expr, "one","two","three","four")
sapply(1:3, num_switch)
#> [1] "one" "two" "three"
num_switch_empty <- function(expr) switch(expr, "one",,"three","four")
sapply(c(1, 3), num_switch_empty) # works. Note the numeric, not an integer!
#> [1] "one" "three"
sapply(c(1,2,3), num_switch_empty) # error because 2 is empty
From the example above we see that type coercion takes place (numeric & integer). This coercion is not identical to as.integer
, see switch(as.raw(1), 100)
. if
does not coerce. Consider a factor,
fct <- as.factor(c("a", "b"))
switch(fct[1], "one", "two") # one + warning
switch(fct[2], "one", "two") # two + warning
if(fct[1]) "one" else "two" # one
if(fct[2]) "one" else "two" # one
Performance
A benchmark1 for increasing n
number of nested expressions. switch
outperforms nested if
quite early on, but overall the performance difference is negligible, even if you somehow write 150 (!) nested if calls. Note that these are for single calls, not for vectors of inputs. The vectorized version of if
is ifelse
. vectorized switch is implement in the kit package.
make_if <- Vectorize(function(x) {
paste(paste0(rep("if(TRUE)", x), collapse = " "), "1L")
})
make_if(10)
#> "if(TRUE) if(TRUE) if(TRUE) if(TRUE) if(TRUE) if(TRUE) if(TRUE) if(TRUE) if(TRUE) if(TRUE) 1L"
make_switch <- Vectorize(function(x) {
paste0("switch(", x, paste0(replicate(x, ","), collapse = ""), 1L, ")")
})
make_switch(10)
[1] "switch(10,,,,,,,,,,1)"
transforming into expressions, and calling bench::mark(iterations = 1e4)
gives
1 don't bet your life on sub-second bench marks