Hagen Brenner
Hagen Brenner

Reputation: 813

if instead of else

I have a simple question: I have got two functions. Both of them use

if(...) {expression}
if(...) {expression}
if(...) {expression}

instead of

if(...) {expression}
else{
      if(...) {expression}
      else {expression}
}

But only the one of both works.

The first one works perfectly:

test.1 <- function (y) {
 if(y == 1){z <- 10}
 if(y == 2){z <- 20}
 if(y == 5){z <- 50}

 return(z)
}

The second one does not work:

df.1 <- data.frame(A = 1:3)
df.2 <- data.frame(A = 4:6)
df.3 <- data.frame(A = 7:9)

 test.2 <- function (num) {
  x <- with(if(num == 1){df.1} 
            if(num == 2){df.2}
            if(num == 3){df.3}, {sum(A)})
return(x)
}

I need to use the if else expressions in order to get the second function work:

test.2 <- function (num) {
  x <- with(if(num == 1){df.1} 
            else {if(num == 2){df.2}
                  else {df.3}
                  }, {sum(A)})
  return(x)
}

I really do not understand why this multiple if statement work in the first case but not in the second one!??

Upvotes: 1

Views: 491

Answers (4)

Richie Cotton
Richie Cotton

Reputation: 121077

Two problems: Firstly, with expects a single expression (that evaulates to a data.frame or environment) in its first argument. You can fix that by wrapping those if statements in curly braces to make it a single expression.

test.2 <- function (num) {
  x <- with(
    {
      if(num == 1){df.1} 
      if(num == 2){df.2}
      if(num == 3){df.3}
    }, 
    sum(A)
  )
  return(x)
}

The second problem is that you aren't returning from the expression after you find a match. So when num is 1, the first if condition is met and the df.1 is returned. But then the next if expression is evaulated, and since num isn't 2, NULL is returned. Similarly, the third if expression returns NULL. Since that is the last expression in the block, that's what gets returned. You effectively have

with(NULL, sum(A))

which is the same as simply sum(A), which throws an error because A doesn't exist.

You could make another fix like

test.2 <- function (num) {
  x <- with(
    {
      if(num == 1)return(df.1) 
      if(num == 2)return(df.2)
      if(num == 3)return(df.3)
    }, 
    sum(A)
  )
  return(x)
}

(this works when num is 1, 2 or 3 ) but you are much better off using switch instead, like James suggests.


If all your data frames are consistently named, as in the example, then there's an even better solution than using switch. Use paste to get the variable name, then call get.

test.3 <- function(num) with(get(paste("df", num, sep = ".")), sum(A))
test.3(1) #6
test.3(2) #15
test.3(3) #24

Upvotes: 1

Joshua Ulrich
Joshua Ulrich

Reputation: 176648

The second example doesn't work because the first argument to with is supposed to be "an environment, a list, a data frame, or an integer as in sys.call" (see the Arguments section ?with).

You can avoid the "unexpected end of line" error by wrapping the if statements in curly brackets:

test.2 <- function (num) {
  x <- with({if(num == 1)df.1 
             if(num == 2)df.2
             if(num == 3)df.3}, {sum(A)})
  return(x)
}

But this still won't work because you have 3 expressions inside the brackets and only the last evaluated expression will be returned. Well, it will work if num==3... but we can do better. You could use switch here instead:

test.2 <- function (num) {
  with(switch(num, df.1, df.2, df.3), sum(A))
}

Upvotes: 4

James
James

Reputation: 66834

You might be better off using switch:

test.2 <- function(num){
 x <- with(switch(num,df.1,df.2,df.3),{sum(A)})
 return(x)
}


test.2(1)
[1] 6
test.2(2)
[1] 15
test.2(3)
[1] 24

Upvotes: 1

Azodious
Azodious

Reputation: 13872

it's possible that all three expressions execute.

if(...) {expression} 
if(...) {expression} 
if(...) {expression} 

In below case, only one expression will execute.

if(...) {expression}  
else
{        
    if(...) {expression}        
    else 
    {
        expression
    }
}

Upvotes: 0

Related Questions