kjo kjo - 2 months ago 6
R Question

Equivalent of Mathematica's Which

Mathematica's

function is a generalized
If
:


Which[test_1, value_1, test_2, value_2, …]


evaluates each of the
test_i
in turn, returning the value of the
value_i
corresponding to the first one that yields
True
.


It's nothing more than a handy way to get rid of superfluous syntax from long sequences of nested simple if-else tests.

Does R have an equivalent function?




BTW, I know that I can always do something like

if (test_1) value_1 else if (test_2) value_2 else ... value_n else default


or, equivalently,

if (test_1) value_1 else
if (test_2) value_2 else
...
if (test_n) value_n else
default


...but, as I already alluded to, when compared to
Which
, nested
if-else
statements bring in a lot of superfluous syntax.

Also, I'm aware of

ifelse(t_1, v_1, ifelse(t_2, v_2, ..., ifelse(t_n, v_n, default)...))


...but the results are sensitive to the shape of the tests, so it is not strictly equivalent to nested
if-else
statements.

Lastly, R's
switch
statement is similar to what I'm looking for, in that it encapsulates a dispatch over a sequence of tests, but it's not quite the same thing. In

switch(expr,
case_1 = value_1,
case_2 = value_2,
...
case_n = value_n,
default)


...the tests are all equality comparisons of
expr
against the
case_i
, whereas in
Which
, etc., the tests are arbitrary boolean expressions.

Answer

You can write your own function which can be used as such a control structure. The followed is based on the fact that match.call supports lazy evaluation. (See this accepted answer):

which.val <- function(...){
  clauses <- match.call(expand.dots = FALSE)$`...`
  n <- length(clauses)
  for(i in seq(1,n,2)){
    condition = eval(clauses[[i]])
    if(condition) return(clauses[[i+1]]) 
  }
}

For testing purposes:

test <- function(a,b){
  print(b)
  a == b
}

The side effect can be used to see what is actually evaluated.

For example:

> x <- 3
> which.val(test(x,1),10,test(x,2),20,test(x,3),30,test(x,4),40)
[1] 1
[1] 2
[1] 3
[1] 30

Note how test(x,4) is never evaluated.