R Conditional evaluation when using the pipe operator %>%

I think that's a case for purrr::when(). Let's sum up a few numbers if their sum is below 25, otherwise return 0.


library("magrittr")
1:3 %>% 
  purrr::when(sum(.) < 25 ~ sum(.), ~0)
#> [1] 6

when returns the value resulting from the action of the first valid condition. Put the condition to the left of ~, and the action to the right of it. Above, we only used one condition (and then an else case), but you can have many conditions.

You can easily integrate that into a longer pipe.


Here is a variation on the answer provided by @JohnPaul. This variation uses the `if` function instead of a compound if ... else ... statement.

library(magrittr)

X <- 1
Y <- TRUE

X %>% `if`(Y, . + 1, .) %>% multiply_by(2)
# [1] 4

Note that in this case the curly braces are not needed around the `if` function, nor around an ifelse function—only around the if ... else ... statement. However, if the dot placeholder appears only in a nested function call, then magrittr will by default pipe the left hand side into the first argument of the right hand side. This behavior is overridden by enclosing the expression in curly braces. Note the difference between these two chains:

X %>% `if`(Y, . + 1, . + 2)
# [1] TRUE
X %>% {`if`(Y, . + 1, . + 2)}
# [1] 4

The dot placeholder is nested within a function call both times it appears in the `if` function, since . + 1 and . + 2 are interpreted as `+`(., 1) and `+`(., 2), respectively. So, the first expression is returning the result of `if`(1, TRUE, 1 + 1, 1 + 2), (oddly enough, `if` doesn't complain about extra unused arguments), and the second expression is returning the result of `if`(TRUE, 1 + 1, 1 + 2), which is the desired behavior in this case.

For more information on how the magrittr pipe operator treats the dot placeholder, see the help file for %>%, in particular the section on "Using the dot for secondary purposes".


Here is a quick example that takes advantage of the . and ifelse:

X<-1
Y<-T

X %>% add(1) %>% { ifelse(Y ,add(.,1), . ) }

In the ifelse, if Y is TRUE if will add 1, otherwise it will just return the last value of X. The . is a stand-in which tells the function where the output from the previous step of the chain goes, so I can use it on both branches.

Edit As @BenBolker pointed out, you might not want ifelse, so here is an if version.

X %>% 
add(1) %>% 
 {if(Y) add(.,1) else .}

Thanks to @Frank for pointing out that I should use { braces around my if and ifelse statements to continue the chain.


It would seem easiest to me to back off from the pipes a little tiny bit (although I would be interested in seeing other solutions), e.g.:

library("dplyr")
z <- data.frame(a=1:2)
z %>% mutate(b=a^2) -> z2
if (z2$b[1]>1) {
    z2 %>% mutate(b=b^2) -> z2
}
z2 %>% mutate(b=b^2) -> z3

This is a slight modification of @JohnPaul's answer (you might not really want ifelse, which evaluates both of its arguments and is vectorized). It would be nice to modify this to return . automatically if the condition is false ... (caution: I think this works but haven't really tested/thought about it too much ...)

iff <- function(cond,x,y) {
    if(cond) return(x) else return(y)
}

z %>% mutate(b=a^2) %>%
    iff(cond=z2$b[1]>1,mutate(.,b=b^2),.) %>%
 mutate(b=b^2) -> z4