2

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
Dan Chaltiel
  • 7,811
  • 5
  • 47
  • 92

2 Answers2

2

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)
}
Lionel Henry
  • 6,652
  • 27
  • 33
  • As I said in my question, "I cannot put it directly in the header for some reason". I really and only want to initialize an `x` variable with `everything()` inside the function. Moreover, your answer surprises me a bit, since this was working fine as in my first code chunk, without curly-curly. – Dan Chaltiel Mar 23 '20 at 08:28
  • It's working fine for selection helpers that rely on the dynamic context. But it'll break down for other kinds of tidyselect features. Also passing contextual variables without `all_of()` or `any_of()` is deprecated, aren't you seeing warnings? – Lionel Henry Mar 23 '20 at 09:16
  • Added example without default. – Lionel Henry Mar 23 '20 at 09:20
  • This is it, it works! Thanks a lot for your help. I saw the warning but I thought I'd deal with it later. You might want to add something like this in the help file, with curly-curly, `all_of()` or `any_of()`, this would be very helpful for folks like me. – Dan Chaltiel Mar 23 '20 at 09:27
1

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 
akrun
  • 874,273
  • 37
  • 540
  • 662
  • I know we can, but it would somehow lack the flexibility I need in this specific case. Isnt there a way to put it in a variable? – Dan Chaltiel Mar 22 '20 at 22:09
  • @DanChaltiel `everything()` outside the tidyverse function context is not evaluated – akrun Mar 22 '20 at 22:13
  • not `var1`, `x`, and not as string, like in my second code chunk – Dan Chaltiel Mar 22 '20 at 22:54
  • @DanChaltiel I updated with a modified option. Please check if that works for you. Here, it is a single `expr` – akrun Mar 22 '20 at 22:56
  • I thank you for your time, but, while all of this is working, this is not what I'm asking as this does not meet the constraints. I really and only want to initialize an `x` variable with `everything()`, inside the function. – Dan Chaltiel Mar 23 '20 at 08:30