rselectdplyrindirection

How to test if R dplyr::select() helper function is NULL inside a function


I know you can use "indirection" when you use dplyr::select() inside a function:

myselect1 <- function( df, select_arg ) {
    df %>%
        select( {{ select_arg }} )
}

starwars %>%
    myselect1( contains( '_color' ) ) %>%
    head( n = 3 )

# hair_color    skin_color  eye_color
# <chr>         <chr>       <chr>
# blond         fair        blue
# NA            gold        yellow
# NA            white, blue red

But, since you can only use a selection helper within a selecting function, if I want to make the selection conditional, I get an error:

myselect2 <- function( df, select_arg = NULL ) {
    if( !is.null( select_arg ) ) {
        df %>%
            select( {{ select_arg }} )
    } else {
        df
    }
}

starwars %>%
    myselect2( contains( '_color' ) ) %>%
    head( n = 3 )

# Error:
# ! `contains()` must be used within a *selecting* function.
# i See <https://tidyselect.r-lib.org/reference/faq-selection-context.html>.
# Backtrace:
#  1. starwars %>% myselect2(contains("_color")) %>% head(n = 3)
#  4. tidyselect::contains("_color")
#  6. tidyselect::peek_vars(fn = "contains")

How could I test whether the selection helper exists or not?


Solution

  • You have to rlang::enquote the function argument. Afterwards you could check for NULL using rlang::quo_is_null:

    library(dplyr, warn=FALSE)
    
    myselect2 <- function(df, select_arg = NULL) {
      if (!rlang::quo_is_null(enquo(select_arg))) {
        df %>%
          select({{ select_arg }})
      } else {
        df
      }
    }
    
    starwars %>%
      myselect2(contains("_color")) %>%
      head(n = 3)
    #> # A tibble: 3 × 3
    #>   hair_color skin_color  eye_color
    #>   <chr>      <chr>       <chr>    
    #> 1 blond      fair        blue     
    #> 2 <NA>       gold        yellow   
    #> 3 <NA>       white, blue red
    
    starwars %>%
      myselect2() %>%
      head(n = 3)
    #> # A tibble: 3 × 14
    #>   name         height  mass hair_…¹ skin_…² eye_c…³ birth…⁴ sex   gender homew…⁵
    #>   <chr>         <int> <dbl> <chr>   <chr>   <chr>     <dbl> <chr> <chr>  <chr>  
    #> 1 Luke Skywal…    172    77 blond   fair    blue         19 male  mascu… Tatooi…
    #> 2 C-3PO           167    75 <NA>    gold    yellow      112 none  mascu… Tatooi…
    #> 3 R2-D2            96    32 <NA>    white,… red          33 none  mascu… Naboo  
    #> # … with 4 more variables: species <chr>, films <list>, vehicles <list>,
    #> #   starships <list>, and abbreviated variable names ¹​hair_color, ²​skin_color,
    #> #   ³​eye_color, ⁴​birth_year, ⁵​homeworld