revaluation

How to use `sprintf()` in LHS during assignment


Preamble

Just to illustrate the problem, let's say I am writing a function that returns TRUE if a column exists in the data frame and produces an error if it doesn't.

We can do this using stopifnot():

does_column_exist <- function(data, col_name) {
  stopifnot("column exists" = col_name %in% colnames(data))
  return(TRUE)
}

does_column_exist(mtcars, "mpg")
#> [1] TRUE
try(does_column_exist(mtcars, "mpg2"))
#> Error in does_column_exist(mtcars, "mpg2") : column exists

Created on 2023-12-03 with reprex v2.0.2

Problem

But note that the error message is vague. If I were to apply this function in a mapper, it'll be difficult to see which column doesn't exist.

To solve this, I can try to customize the name for the provided expression to contain the column name:

does_column_exist2 <- function(data, col_name) {
  stopifnot(sprintf("%s column exists", col_name) = col_name %in% colnames(data))
  return(TRUE)
}

But this code isn't syntactically valid (Error: unexpected '=').

So I am wondering if there is any way to use sprintf() in LHS of an assignment? If not, how could one customize the name for expression in stopifnot() calls?


Solution

  • You cannot evaluate something on the left hand side of = or <-. That's also the reason why people use assign or data.table makes use of the := operator.

    What you can do instead? Usually do.call comes in handy in such situations. Here, since stopifnot() works with named arguments, setNames() is also an option:

    does_column_exist2 <- function(data, col_name) {
      # setNames
      stopifnot(setNames(nm = sprintf("%s column exists", col_name),
                         object = col_name %in% colnames(data)))
      
      # do.call with setNames(). 
      # bit unessecary here but do.call works in many situations in which evaluated 
      # arguments should be used
      do.call(stopifnot, list(setNames(nm = sprintf("%s column exists", col_name), 
                                       object = col_name %in% colnames(data))))
      return(TRUE)
    }