rfunctionr-glue

Using glue_data() in a function works with some column names and not others


I'm working on a function that takes a dataframe and user-specified columns as inputs and uses glue_data() to output some nicely formatted text.

Whether or not a variable works seems to be the luck of the draw, but is consistent from attempt to attempt. e.g. with mtcars, mpg always works, and hp never does:

library(glue)
data <- head(mtcars)

print_messages <- function(x=NULL, att1=NULL, att2=NULL) {
  
  # This if-else seems to be the problem
  if(is.null(att1)) {
    att1_glue <- ""
  } else {
    att1_glue <- paste0("{", deparse(substitute(att1)),"}")
  }
  
  if(is.null(att2)) {
    att2_glue <- ""
  } else {
    att2_glue <- paste0("{", deparse(substitute(att2)),"}")
  }
  
  pattern <-   paste("", att1_glue,"", att2_glue)        
  
  glue_data(x, pattern)
  
}

print_messages(data, att1=mpg, att2=NULL) 

# 21  
# 21  
# 22.8  
# 21.4  
# 18.7  
# 18.1  

print_messages(data, att1=mpg, att2=hp)
#> Error in print_messages(data, att1 = mpg, att2 = hp): object 'hp' not found

This is not a function of which variable goes in att1 vs att2

I have figured out that the if-else is the problem, but don't know how to fix it. It was inserted because I can't just have a null argument -- this makes glue_data() return a 0-character string:

print_messages <- function(x=NULL,att1=NULL, att2=NULL) {
  
  att1_glue <- paste0("{", deparse(substitute(att1)),"}")
  att2_glue <- paste0("{", deparse(substitute(att2)),"}")
  
  pattern <-   paste("",att1_glue,"", att2_glue)        
  
  glue_data(x, pattern)
  
}


print_messages(data, att1=mpg, att2=hp )
# 21  110
# 21  110
# 22.8  93
# 21.4  110
# 18.7  175
# 18.1  105

print_messages(data, att1=hp, att2=NULL )


I have tried other approaches to using the argument from the function in glue_data(), like from this answer. But the central issue seems to be with the if-else.

I'm wondering what's going on there? Even if there is a way to fix this on the glue side, I'd be interested in why the parsing works with some columns and not others!

Thank you for your help!


Solution

  • Evaluation of non-existing objects will throw an error.

    f <- function(x = NULL) if(is.null(x)) cat("hi") else cat("bye")
    f(mpg) # error
    f("mpg")
    #> bye
    f()
    #> hi
    

    The first function should throw an error, do you perhaps have an object called mpg in your environment? In that case lexical scoping will find it, and yield is.null(mpg) as FALSE.

    An alternative is to use dots ..., and using the vectorized nature of paste, avoiding the need for pre-filled arguments with NULL.

    print_messages <- function(data, ...){
      expr = paste0("{", 
                    vapply(substitute(...()), deparse, "")
                    , "}", collapse = " ")
      glue_data(data, expr)
    }