`r`/`rlang`/`dplyr`: How to render `sym` resilient to `NULL`?

Viewed 116

Edited in response to @akrun's insight:

This works:

require("magrittr")
requireNamespace("dplyr")

df <- data.frame(a = 1:5)

b_column <- c_column <- "a"
df %>% dplyr::mutate(
    b = !!dplyr::sym(b_column),
    c = !!dplyr::sym(c_column))

But when any one of the *_columns is NULL it doesn't:

c_column <- NULL
df %>% dplyr::mutate(
    b = !!dplyr::sym(b_column),
    c = !!dplyr::sym(c_column))

The resulting error is:

Error: Only strings can be converted to symbols
Run `rlang::last_error()` to see where the error occurred.

How would I make the call to ANY of the ensymboled *_column variables resilient to it being NULL?

1 Answers

If we need to check for NULL case, use an if condition

df1 <- if(!is.null(c)) {
       df %>%
          dplyr::mutate(b = !!dplyr::sym(c))
         } else df

With multiple columns, an option is map

library(purrr)
b_column <- c_column <- "a"
map2_dfc(list(b_column, c_column), c("b", "c"), ~ 
    if(!is.null(.x)) df %>% 
               transmute(!! .y := !! sym(.x))) %>% 
  bind_cols(df, .)

-output

#  a b c
#1 1 1 1
#2 2 2 2
#3 3 3 3
#4 4 4 4
#5 5 5 5

If one of them is NULL

c_column <- NULL
map2_dfc(list(b_column, c_column), c("b", "c"), ~  
         if(!is.null(.x)) df %>%
                     transmute(!! .y := !! sym(.x))) %>%
  bind_cols(df, .)
#  a b
#1 1 1
#2 2 2
#3 3 3
#4 4 4
#5 5 5

Another option is mutate with across, but make sure that we need to rename only the columns that are not NULL

nm1 <- c("b", "c")
i1 <-  !map_lgl(list(b_column, c_column), is.null)
nm2 <- nm1[i1]
df %>% 
   mutate(across(all_of(c(b_column, c_column)), ~ .)) %>% 
   rename_at(vars(everything()), ~ nm2) %>% 
   bind_cols(df, .)
Related