You can simplify a bit by using ..n
notation to refer to the n
th term of ...
:
library(dplyr)
conditionally <- function(fun){
function(..., execute) {
if (execute) fun(...) else ..1
}
}
mtcars %>%
conditionally(filter)(hp == 245, execute = TRUE) %>%
conditionally(select)(cyl, execute = FALSE)
#> mpg cyl disp hp drat wt qsec vs am gear carb
#> 1 14.3 8 360 245 3.21 3.57 15.84 0 0 3 4
#> 2 13.3 8 350 245 3.73 3.84 15.41 0 0 3 4
Without writing your own adverbial function, you can do this inline with braces to control where the incoming data .
is passed:
mtcars %>%
{if (TRUE) filter(., hp == 245) else .} %>%
{if (FALSE) select(., cyl) else .}
#> mpg cyl disp hp drat wt qsec vs am gear carb
#> 1 14.3 8 360 245 3.21 3.57 15.84 0 0 3 4
#> 2 13.3 8 350 245 3.73 3.84 15.41 0 0 3 4
With a little rlang magic, you can write a version of conditionally
that does the same thing, even taking raw expressions for the condition and call and evaluating each in the context of the data:
provided <- function(data, condition, call) {
if (rlang::eval_tidy(enquo(condition), data)) {
rlang::eval_tidy(rlang::quo_squash(quo(data %>% !!enquo(call))))
} else data
}
mtcars %>%
provided(all(cyl > 0), filter(hp == 245)) %>%
provided(any(cyl < 0), select(cyl))
#> mpg cyl disp hp drat wt qsec vs am gear carb
#> 1 14.3 8 360 245 3.21 3.57 15.84 0 0 3 4
#> 2 13.3 8 350 245 3.73 3.84 15.41 0 0 3 4
As far as API structure, I worry that implicitly passing .
into the call
parameter is inconsistent with the pipe's expected behavior, but the code does read nicely.