Reputation: 27027
As the question asks, is there a control sequence in R similar to C's ternary operator? If so, how do you use it? Thanks!
Upvotes: 227
Views: 89412
Reputation: 347
tidyverse has if_else() function in the dplyr library. Compared to the base ifelse(), this function is more strict. It checks that true and false are the same type. This strictness makes the output type more predictable, and makes it somewhat faster.
if_else(a == 1, 1, 2)
Upvotes: 4
Reputation: 354
I have written a small language extension which emulates C's conditional ternary operator in R. It can be installed as a package from here
The implementation is based on the answer given by @kohske, but I have made some changes so that it is robust to cases when the if_true and if_false arguments contain a colon, allows conditional statements to be chained and retains the base functionality of the ?
operator.
I will refer to others' warnings about the dangers of redefining operators, but it's a neat example of how dynamic a language R can be!
Upvotes: 1
Reputation: 558
if
works like unvectorised ifelse if used in following manner:
`if`(condition, doIfTrue, doIfFalse)
The advantage of using this over ifelse is when the vectorisation is in the way (i.e I have scalar boolean and list/vector things as a result)
ifelse(TRUE, c(1,2), c(3,4))
[1] 1
`if`(TRUE, c(1,2), c(3,4))
[1] 1 2
Upvotes: 11
Reputation: 60944
I would take a look at the ifelse
command. I would call it even better because it is also vectorized. An example using the cars dataset:
> cars$speed > 20
[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[25] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[37] FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE TRUE
[49] TRUE TRUE
> ifelse(cars$speed > 20, 'fast', 'slow')
[1] "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow"
[11] "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow"
[21] "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow"
[31] "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow" "slow"
[41] "slow" "slow" "slow" "fast" "fast" "fast" "fast" "fast" "fast" "fast"
Upvotes: 8
Reputation: 121077
Like everyone else said, use ifelse
, but you can define operators so that you nearly have the ternary operator syntax.
`%?%` <- function(x, y) list(x = x, y = y)
`%:%` <- function(xy, z) if(xy$x) xy$y else z
TRUE %?% rnorm(5) %:% month.abb
## [1] 0.05363141 -0.42434567 -0.20000319 1.31049766 -0.31761248
FALSE %?% rnorm(5) %:% month.abb
## [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
# or, more generally
condition %?% value1 %:% value2
It actually works if you define the operators without the %
signs, so you could have
`?` <- function(x, y) if(x) y[[1]] else y[[2]]
`:` <- function(y, z) list(y, z)
TRUE ? rnorm(5) : month.abb
## [1] 1.4584104143 0.0007500051 -0.7629123322 0.2433415442 0.0052823403
FALSE ? rnorm(5) : month.abb
## [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"
(This works because the precedence of :
is lower than ?
.)
Unfortunately, that then breaks the existing help and sequence operators.
Upvotes: 27
Reputation: 40821
Just as a prank, you can redefine the ?
operator to (almost) work like the ternary operator (THIS IS A BAD IDEA):
`?` <- function(x, y) { y <-substitute(y); if(x) eval(y[[2]], parent.frame()) else eval(y[[3]], parent.frame()) }
x <- 1:3
length(x) ? (x*2) : 0
x <- numeric(0)
length(x) ? (x*2) : 0
for(i in 1:5) cat(i, (i %% 2) ? "Odd\n" : "Even\n")
... But you need to put the expressions in parentheses because the default precedence isn't like in C.
Just remember to restore the old help function when you're done playing:
rm(`?`)
Upvotes: 6
Reputation: 66862
As if
is function in R
and returns the latest evaluation, if-else is equivalent to ?:
.
> a <- 1
> x <- if(a==1) 1 else 2
> x
[1] 1
> x <- if(a==2) 1 else 2
> x
[1] 2
The power of R is vectorization. The vectorization of the ternary operator is ifelse
:
> a <- c(1, 2, 1)
> x <- ifelse(a==1, 1, 2)
> x
[1] 1 2 1
> x <- ifelse(a==2, 1, 2)
> x
[1] 2 1 2
Just kidding, you can define c-style ?:
:
`?` <- function(x, y)
eval(
sapply(
strsplit(
deparse(substitute(y)),
":"
),
function(e) parse(text = e)
)[[2 - as.logical(x)]])
here, you don't need to take care about brackets:
> 1 ? 2*3 : 4
[1] 6
> 0 ? 2*3 : 4
[1] 4
> TRUE ? x*2 : 0
[1] 2
> FALSE ? x*2 : 0
[1] 0
but you need brackets for assignment :(
> y <- 1 ? 2*3 : 4
[1] 6
> y
[1] 1
> y <- (1 ? 2*3 : 4)
> y
[1] 6
Finally, you can do very similar way with c:
`?` <- function(x, y) {
xs <- as.list(substitute(x))
if (xs[[1]] == as.name("<-")) x <- eval(xs[[3]])
r <- eval(sapply(strsplit(deparse(substitute(y)), ":"), function(e) parse(text = e))[[2 - as.logical(x)]])
if (xs[[1]] == as.name("<-")) {
xs[[3]] <- r
eval.parent(as.call(xs))
} else {
r
}
}
You can get rid of brackets:
> y <- 1 ? 2*3 : 4
> y
[1] 6
> y <- 0 ? 2*3 : 4
> y
[1] 4
> 1 ? 2*3 : 4
[1] 6
> 0 ? 2*3 : 4
[1] 4
These are not for daily use, but maybe good for learning some internals of R language.
Upvotes: 368
Reputation: 176668
It doesn't explicitly exist, but you can do:
set.seed(21)
y <- 1:10
z <- rnorm(10)
condition1 <- TRUE
x1 <- if(condition1) y else z
or
condition2 <- sample(c(TRUE,FALSE),10,TRUE)
x2 <- ifelse(condition2, y, z)
The difference between the two is that condition1
must be a logical vector of length 1, while condition2
must be a logical vector the same length as x
, y
, and z
. The first will return either y
or z
(the entire object), while the second will return the corresponding element of y
(condition2==TRUE
) or z
(condition2==FALSE
).
Also note that ifelse
will be slower than if
/ else
if condition
, y
, and z
are all vectors with length 1.
Upvotes: 4
Reputation: 179438
Your link points to an if
statement.
> x <- 1
> if(x < 2) print("Less than") else print("Greater than")
[1] "Less than"
If your input variable is a vector, then ifelse
might be more suitable:
> x <- 1:3
> ifelse(x<=2, "Less than or equal", "Greater than")
[1] "Less than or equal" "Less than or equal" "Greater than"
To access the help page for if
, you need to embed the if
in backticks:
?`if`
The help page for ifelse
is at:
`?ifelse`
Upvotes: 4