I'm trying to nest a function that glues together two strings within a function that uses the combined string to name a column of a dataframe. However, the problem seems to be that the glue expression is not evaluated to a string early enough. Can (and should) I force the expression to be evaluated before it is being passed on as an argument to another function?
library(tidyverse)
# define inner function
add_prefix <- function(string) {
x <- glue::glue("prefix_{string}")
return(as.character(x))
}
# define outer function
mod_mtcars <- function(df, name) {
df %>%
mutate({{ name }} := mpg ^ 2)
}
mod_mtcars(mtcars, add_prefix("foo"))
#> Error: The LHS of `:=` must be a string or a symbol
# alternative outer function with explicit enquoting
mod_mtcars2 <- function(df, name) {
name <- ensym(name)
df %>%
mutate(!!name := mpg ^ 2)
}
mod_mtcars2(mtcars, add_prefix("foo"))
#> Error: Only strings can be converted to symbols
Created on 2021-10-30 by the reprex package (v2.0.1)
First of all, the glue string syntax is now preferred over embracing directly in the LHS. So prefer "{{ var }}" := expr
to {{ var }} := expr
. In a future version of rlang (next year) we'll make it possible to use glue strings with =
. At that point, :=
will be pretty much superseded. We went with :=
to allow !!
injection on the LHS before glue support was added.
Second, your problem is that you're using {{
instead of simple injection. {{
is for injecting the expression supplied as argument, not the value of the expression. Use normal glue interpolation with "{"
to inject the value instead:
mod_mtcars <- function(df, name) {
df %>%
mutate("{name}" := mpg ^ 2)
}
PS: Your !!
version had a similar problem. Because you used ensym()
on the argument, you were defusing the expression supplied as argument instead of using the value. But ensym()
requires the expression to be a simple name and you supplied a full computation, causing an error. You can fix it like this:
mod_mtcars2 <- function(df, name) {
df %>%
mutate(!!name := mpg ^ 2)
}
But glue syntax is now preferred.
name
is not a symbol. Try:
mod_mtcars <- function(df, name) {
name <- sym(name)
df %>%
mutate({{ name }} := mpg ^ 2)
}
mod_mtcars(mtcars, add_prefix("foo"))
Even better, tidy eval now supports glue strings so you could simplify with:
# don't need add_prefix()
mod_mtcars <- function(df, name){
df %>%
mutate("prefix_{{name}}" := mpg ^ 2)
}
mod_mtcars(mtcars, foo)
Lastly, the curly-curly operator tunnels your expression and you're passing it a string. If you keep your current functions use the bang-bang operator instead:
mod_mtcars <- function(df, name){
df %>%
mutate(!! name := mpg ^ 2)
}
mod_mtcars(mtcars, add_prefix("foo"))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With