Search code examples
rrlangtidyeval

Convert `c(x, y)` as function argument to `c("x", "y")` in R (rlang/tidyevaluation)


I want to let users of my function enter variables in the tidyeval data-masking way i.e. fun(x, c(y, z)) as opposed to fun("x", c("y", "z")), but when they enter arguments like c(y, z), I want to be able to use this as a character vector, like c("y", "z").

I do have a seriously overkill workaround for working with column names. That is, I use {{ in a dpylr::select() call and then extract the column names:

fun_overkill <- function(data, cols) {
  data_subset <- dplyr::select(data, {{cols}})
  
  args_as_character_vector <- colnames(data_subset)
  
  args_as_character_vector
}

fun_overkill(mtcars, c(mpg, drat))

Clearly, this cannot be the best way, and it does not generalise to a situation where my vector argument doesn't represent column names.

I suspect {rlang} may have the answer but there are so many choices and I do not understand the terminology.

A situation where this could be useful is in creating a model formula, as you can use as.formula on a string, which you create from the character vector:

rhs <- paste(fun_overkill(mtcars, c(mpg, drat)), collapse = " + ")
lhs <- "~ "
as.formula(paste0(lhs, rhs))

Solution

  • One way is to capture the cols variables of an expression with enexpr() (enquo() would work too), and then get the arguments from the expression with call_args(). These are now a list of symbols, so you need to loop over them to make them characters with as_string().

    library(rlang)
    
    fun <- function(data, cols) {
      vars <- call_args(enexpr(cols))
      vapply(vars, as_string, character(1), USE.NAMES = FALSE)
    }
    
    (fun(mtcars, c(mpg, drat)))
    #> [1] "mpg"  "drat"
    
    
    rhs <- paste(fun(mtcars, c(mpg, drat)), collapse = " + ")
    lhs <- "~ "
    as.formula(paste0(lhs, rhs))
    #> ~mpg + drat
    

    Created on 2021-09-03 by the reprex package (v2.0.1)

    To be fair, the data argument isn't needed at all here. It's just there because it was there in your example.