rdplyrtidyeval

pass values through to piped dplyr verb inside if{} statement in function


Say I have this data:

d <- msleep %>% 
      mutate(comfort_color_desk = sample(c(0,1), replace=TRUE, size=nrow(msleep)),
             comfort_color_chair = sample(c(0,1), replace=TRUE, size=nrow(msleep))) 

I want to pass it through to a dplyr chain that lives inside a function i've made, which also includes an indicator to trigger an {if} clause inside of the chain.

For example (this works just fine):

test_fcn <- function(.x, .y, .count = NULL) {
  .x %>% select(order, .y) %>%
    {if(isTRUE(.count)) count(.,!!ensym(.y)) else .} 
}

d %>% test_fcn(., .y = "comfort_color_desk", .count = TRUE)

This shows the basic principle of what I want to do. The problem i'm facing is when I want to just pass through a part of column name and paste that with a string and evaluate it as a data column inside the function.

For example (this does not work):

test_2_fcn <- function(.x, .y, .z, .compare = NULL) {
  .x %>% 
    {if(isTRUE(.compare)) filter(., paste0("comfort_color_", !!ensym(.y)) == 1 & paste0("comfort_color", !!ensym(.z)) == 1) else .} 
}

d %>% test_2_fcn(., .y = "desk", .z = "chair", .compare = TRUE)

I suspect this is a problem with tidy evaluation. Can anyone point in the right direction here please?


Solution

  • This is an issue of tidy evaluation. paste0 function is being evaluated before the !!ensym() is evaluated, now it is tried to paste the literal string "comfort_color_" with the symbol .y or .z instead of the values.

    We could use the sym() function to convert the string to a symbol and then use paste() to concatenate the strings together.

    library(dplyr)
    
    test_2_fcn <- function(.x, .y, .z, .compare = NULL) {
      .x %>% 
        {if(isTRUE(.compare)) filter(., !!sym(paste0("comfort_color_", .y)) == 1 & !!sym(paste0("comfort_color_", .z)) == 1) else .} 
    }
    
    d %>% test_2_fcn(., .y = "desk", .z = "chair", .compare = TRUE)