How to initialize a variable with a tidyselect helper?

Viewed 92

I'm using tidyselection in some function, and I have to concatenate the first argument with the ellipsis as it could be a specific class that would need a specific treatment.

The normal behavior is this:

foo = function(x, ...){
    xloc = eval_select(expr(c(x, ...)), data = iris)
    return(xloc)
}
foo(everything())

I want to have everything() as the default value when x is NULL (I cannot put it directly in the header for some reason).

Unfortunately, this syntax is not allowed:

bar = function(x, ...){
    if(is_null(x))
        x=everything() #throws an error
    xloc = eval_select(expr(c(x, ...)), data = iris)
    return(xloc)
}
bar(NULL)
# Error: `everything()` must be used within a *selecting* function.
# i See <https://tidyselect.r-lib.org/reference/faq-selection-context.html>.

I tried to wrap everything() with all "mystic" functions I know: parse, deparse, call, substitute, quo, sym, enquo, ensym, ... Nothing worked (you can see here that I don't master these very well).

With what expression can I replace my x=everything() line in my second code chunk for this function to work?

Versions:

  • tidyselect version 1.0.0
  • rlang version 0.4.5
  • dplyr version 0.8.5
2 Answers

First you need to pass x via {{, otherwise the argument can't be inspected by tidyselect, and some features won't work properly. Then you can give it a default of everything():

foo <- function(x = everything(), ...) {
  eval_select(expr(c({{ x }}, ...)), data = iris)
}

foo(everything())
#> Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species
#>            1            2            3            4            5

foo()
#> Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species
#>            1            2            3            4            5

If you can' use default arguments for some reason, defuse everything() manually then force it with !!:

foo <- function(x = NULL, ...) {
  x <- enquo(x)

  if (quo_is_null(x)) {
    x <- expr(everything())
  }

  eval_select(expr(c(!!x, ...)), data = iris)
}

We can wrap the everything within the eval_select

bar <- function(x, ...){

    xloc <- tidyselect::eval_select(expr(c(x, ...)), data = iris)
    if(length(xloc) == 0) {
     xloc <- tidyselect::eval_select(expr(everything()), data = iris)
      }
    xloc
}



bar(1:2)
#Sepal.Length  Sepal.Width 
#           1            2 
bar(NULL)
#Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
#           1            2            3            4            5 

Or we can have the if/else condition within the expr

bar <- function(x, ...) {

    x1 <-  expr(c(if(is_null(x)) everything() else x, ...))
    tidyselect::eval_select(x1, data = iris)

}

bar(everything())
#Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
#           1            2            3            4            5 
bar(NULL)
#Sepal.Length  Sepal.Width Petal.Length  Petal.Width      Species 
#           1            2            3            4            5 
Related