Alex Rossell Hayes
Alex Rossell Hayes

Reputation: 61

Can a function detect whether it has been piped into?

Is there a way for a function to detect whether a magrittr pipe has passed data into it?

A motivating example:

Certain functions like ifelse() don't work well with magrittr input.

1:4 %>% ifelse(. < 3, "low", "high")
#> Error in ifelse(., . < 3, "low", "high") : unused argument ("high")

results in an error.

To make the function work as intended, you have to use

1:4 %>% {ifelse(. < 3, "low", "high")}
#> "low"  "low"  "high" "high"

Is there a way to redefine a function so that it behaves differently when piped into?

My best attempt:

my_ifelse <- function(...) {
  args <- list(...)

  if (sys.call()[[2]] == ".") args <- args[-1]

  ifelse(test = args[[1]], yes = args[[2]], no = args[[3]])
}

1:4 %>% my_ifelse(. < 3, "low", "high")
#> "low"  "low"  "high" "high"

Are there any issues with this solution? Could it break unexpectedly?

Edit: Obviously, this will return an error if an incorrect number of arguments are passed. A more full-fledged approach would attempt to provide informative errors and even potentially allow use argument names rather than ...

Upvotes: 0

Views: 106

Answers (1)

Ronak Shah
Ronak Shah

Reputation: 388962

When we use pipe we know that the left-hand side of pipe is treated as the first argument to the function on the right-hand side.

So you can't expect this to work :

1:4 %>% ifelse(. < 3, "low", "high")

Since what this actually means is

ifelse(1:4, 1:4 < 3, "low", "high")

which gives you the exact same error

Error in ifelse(1:4, 1:4 < 3, "low", "high") : unused argument ("high")

Yes, you can stop this default behavior by using {} around pipes.

I am not sure what your final goal is but a general rule which can be followed is all the changes that you want to do on the input should be done on LHS side only unless you want to use {}.

So this works :

(1:4 < 3) %>% ifelse("low", "high")
#[1] "low"  "low"  "high" "high"

Upvotes: 2

Related Questions