3

The by argument in dplyr::left_join (and friends) expects a list of strings. My code already defines the variables of interest as a list of quosures so I want to avoid writing the list down a second time by converting the list of quosures to a list of strings.

I went hunting around the rlang docs but did not find an existing solution. The quo_expr family of functions can splice and convert quosures to a single string but they do not produce a list of strings:

library(rlang)

# this works
sepal_length <- quo(Sepal.Length)
quo_text(sepal_length)

# this doesnt
sepaldims <- quos(Species, Sepal.Length)
quo_text(sepaldims)

Here's an example where this would be useful:

library(dplyr)

# group_by accepts a list of quosures
sepaldims <- quos(Species, Sepal.Length)
iris_sepal <- iris %>%
  group_by(!!!sepaldims) %>%
  summarize(sepal_width = mean(Sepal.Width), petal_width = mean(Petal.Width))    
petaldims <- quos(!!!sepaldims, Petal.Length)
iris_petal <- iris %>%
  group_by(!!!petaldims) %>%
  summarize(sepal_width = mean(Sepal.Width), petal_width = mean(Petal.Width))

# but left join expects a list of strings
iris_merge <- left_join(iris_sepal, iris_petal, by = c("Species", "Sepal.Length")) 
Stanwood
  • 268
  • 1
  • 10

1 Answers1

3

The solution I came up with applies quo_text to the list:

quos_text <- function(qs) {
  unlist(lapply(seq_along(qs), function(i) quo_text(qs[[i]])))
}

Now we can handle lists of quosures:

sepaldims <- quos(Species, Sepal.Length)
petaldims <- quos(!!!sepaldims, Petal.Length)

quos_text(sepaldims)
quos_text(petaldims)
quos_text(quos(!!!sepaldims, Petal.Width))

And left_join is satisfied:

# these two are equivalent
iris_merge <- left_join(iris_sepal, iris_petal, by = c("Species", "Sepal.Length"))
iris_merge2 <- left_join(iris_sepal, iris_petal, by = quos_text(sepaldims))

@Marius has pointed out that there is a simpler form we can use:

sapply(qs, quo_text)

This is short enough I will probably use it inline rather than as a custom function.

Stanwood
  • 268
  • 1
  • 10