I am trying to implement a pipeline that has an optional step which consists of a pipeline of several functions. It runs this pipeline based on a condition, and otherwise it just passes through the original value. However I've tried implementing this using if
and also purrr::when
, but in both cases the positive case simply returns the pipeline instead of executing it.
Here's a simple contrived example. I've simplified the conditional pipeline to only one function, but being able to use magrittr pipes inside the TRUE
branch is important here.
library(magrittr)
maybe_round = function(x, round = TRUE){
x %>%
`if`(
round,
. %>% round(),
.
)
}
> maybe_round(5.3, TRUE)
Functional sequence with the following components:
1. round(.)
Use 'functions' to extract the individual functions.
> maybe_round(5.3, FALSE)
[1] 5.3
This second case is working correctly; it's returning the original value unchanged. But this first case is not, it's returning the magrittr pipeline, but not actually feeding it with 5
. How can I get this to work as I intend? I assume this has something to do with how magrittr
rewrites the syntax tree, but I can't quite work it out.
CodePudding user response:
The syntax . %>% round(.)
means function(.) round(.)
. Any time dot starts a pipeline it defines a function rather than being an ordinary pipeline. Put parentheses around the dot to prevent the dot from starting the inner pipeline.
maybe_round = function(x, round = TRUE){
x %>%
`if`(
round,
(.) %>% round(),
.
)
}
maybe_round(5.3, TRUE)
## [1] 5
Another possibility is to just leave it as a function and then evaluate that function at the outer dot like this:
maybe_round = function(x, round = TRUE){
x %>%
`if`(
round,
(. %>% round())(.),
.
)
}
CodePudding user response:
This is unrelated to `if`
— the same issue would manifest with any other function. In fact, the issue is the . %>% …
expression, which is not a normal pipeline. Instead, this special syntax creates a lambda (see “Using the dot-place holder as lhs
” in the documentation).
If you insist on using a pipe here, you’ll need to first assign .
to a different variable name, e.g.:
maybe_round = function(x, round = TRUE){
x %>%
`if`(
round,
{
x = .
x %>% round()
},
.
)
}
… honestly, I would instead use a regular if
expression and encapsulate it inside a function that can be piped into, i.e.
maybe_round = function (x, round = TRUE) {
x %>% maybe_round_impl(round)
}
maybe_round_impl = function (x, round = TRUE) {
if (round) {
x %>% round()
} else {
x
}
}
CodePudding user response:
Just don't pipe again in the `if`()
.
maybe_round = function(x, round = TRUE){
x %>%
`if`(
round,
round(.),
.
)
}
maybe_round(5.3, TRUE)
# [1] 5
maybe_round(5.3, FALSE)
# [1] 5.3
CodePudding user response:
Using the native pipe |>
we can circumvent the behavior without declaring a separate variable:
when the magrittr pipe is to be used for further manipulations
maybe_round <- \(x, round = TRUE){
x %>%
`if`(
round,
. |>
round() %>%
` `(., 1), # random thing to prove it works
.
)
}
maybe_round(5.3, F)
# >[1] 5.3
maybe_round(5.3, T)
#> [1] 6