0

I am writing a generalized lookup function to execute against a tibble. When I run the code below, I get "Error: object 'x' not found"

My real function returns a different error message, but I think some guidance on this will help.

See the code below

library(dplyr)
library(tibble)

fruits <- tibble(
  x = 1:5, 
  y = c("apple", "peach", "pear", "strawberry", "orange")
)

gLookup <- function(datasource, indexColumn, targetValue, lookupColumn){
  datasource %>% 
    filter(indexColumn == targetValue) %>% 
    select(lookupColumn) %>% 
    unlist() %>% 
    unname
}

gLookup(fruits, x, 3, y)

I expect "pear" to be returned, but instead I get: Error: object 'x' not found

camille
  • 16,432
  • 18
  • 38
  • 60
Charles Knell
  • 117
  • 1
  • 9
  • Can't find a more suitable duplicate. Unless things have changed, `dplyr` uses `NSE` hence you need such things as `!!` and `enquo`, `sym` etc. – NelsonGon Jul 04 '19 at 16:51
  • Or the new `{{` as in `filter({{indexColumn}} == {{targetValue}}) %>%`. The same should be done for `lookupColumn`. – Rui Barradas Jul 05 '19 at 06:29

1 Answers1

2

Writing functions with dplyr is a little complicated because of its non-standard evaluation. It has a solid framework behind it, but it's a little work to learn. For the problem at hand, you need to substitute and quote the column names you're passing in (with rlang::enquo), and then unquote when you want to use them (with !!).

library(dplyr)

fruits <- tibble::tibble(
    x = 1:5, 
    y = c("apple", "peach", "pear", "strawberry", "orange")
)

gLookup <- function(datasource, indexColumn, targetValue, lookupColumn){
    indexColumn <- enquo(indexColumn)    # substitute and quote
    lookupColumn <- enquo(lookupColumn)

    datasource %>% 
        filter(!!indexColumn == targetValue) %>%    # unquote with !!
        select(!!lookupColumn) %>% 
        unlist() %>% 
        unname
}

gLookup(fruits, x, 3, y)
#> [1] "pear"

If you've got the new version of rlang, you can substitute, quote, and unquote all in one with {{...}}:

gLookup <- function(datasource, indexColumn, targetValue, lookupColumn){
    datasource %>% 
        filter({{indexColumn}} == targetValue) %>%    # both substitute and quote with `{{...}}`
        select({{lookupColumn}}) %>% 
        unlist() %>% 
        unname
}

gLookup(fruits, x, 3, y)
#> [1] "pear"
alistaire
  • 42,459
  • 4
  • 77
  • 117
  • 1
    Excellent! I never would have worked it out on my own. This also solves the problem in the "real" function. Thank you. – Charles Knell Jul 04 '19 at 17:18
  • My problem was solved simply by using the {{}} operator: datasource %>% filter({{indexColumn}} == targetValue) %>% select({{lookupColumn}}) %>% unlist() %>% unname – Charles Knell Jul 04 '19 at 17:21
  • Something about the"substitute,quote" part, I literally translated it into `substitute(quote())` which will not work. – NelsonGon Jul 05 '19 at 05:26
  • `substitute` is sort of like `enquo` (and sometimes `{{...}}`); `quote` is like `quo`. There's not really a base equivalent of `!!`; maybe `.(...)` in `bquote`. I'm not sure the point of using base NSE when rlang (whose quosures are more robust than raw expressions) is already built into dplyr, though. – alistaire Jul 05 '19 at 05:48