help with tidyeval function


#1

Hi
I wrote this function a while back, and now it suddenly doesn't work anymore. I'm guessing theres been an update somewhere that broke it, and I can't seem to figure out what is going on.

xgather <- function(x, col1, col2, newcol) {
  col1 <- enquo(col1)
  col2 <- enquo(col2)
  newcol_name <- quo_name(newcol)
  
  
  mutate(x, !! newcol_name := case_when(
    type == "method1" ~ !! col1,
    type == "method2" ~ !! col2))
}

test <- test %>% 
  xgather(randomcol1, randomcol2, "new_column_name")

Error in .f(.x[[i]], ...) : object 'new_column_name' not found

Any suggestions? Thank you!


#2

This solved it. Source (https://tidyeval.tidyverse.org/getting-up-to-speed.html)

 newcol_name <- enquo(newcol)

Tidyeval is pretty hard to understand for a beginner; should I just assume it's correct if it works, or is there a "better" way to do this?


#3

Expanded a bit, this might make a good topic on its own. Since you solved your problem in this topic (and it’s marked as solved — thanks! :grinning: ), I suspect not very many people will see your question here.


#4

If you're passing in the new column name as a string, you don't really need to do anything at all (neither enquo nor quo_name); the left-hand side of := is happy with it already.

If you're passing in an unquoted expression, you'll need to use enquo to prevent it from being evaluated (evaluation would cause an error, as the column doesn't exist yet!). Once the expression is collected to a quosure with enquo, quo_name converts the quosure to a string, which makes it equivalent to passing a string directly.

However, := will also take a quosure for a name, so quo_name is superfluous here. In some ways it's worse—if you pass in a string by accident, it will get over-quoted, resulting in a column name with quotes in it. If you just use enquo, it will work either way.

...but yes, all this stuff is a little complicated. It's useful in some contexts, but beginners should be able to avoid it for the most part. One way to simplify lots of cases is to write functions that operate on columns, not data frames, so you can just call them explicitly inside of mutate, which lets you set the column name as usual.