6

I'd like to use a utility function to check whether a given column exists within a given data.frame. I'm piping within the tidyverse. The best I've come up with so far is

library(magrittr)

columnExists <- function(data, col) {
  tryCatch({
    rlang::as_label(rlang::enquo(col)) %in% names(data)
    },
    error=function(e) FALSE
  )
}

This works in the global environment

> mtcars %>% columnExists(mpg)
[1] TRUE
> mtcars %>% columnExists(bad)
[1] FALSE

But not when called from within another function, which is my actual use case

outerFunction <- function(d, col) {
  d %>% columnExists((col))
}
> mtcars %>% outerFunction(mpg)  # Expected TRUE
[1] FALSE
> mtcars %>% outerFunction(bad) # Expected FALSE
[1] FALSE

What am I doing wrong? Is it possible to have a single function that works correctly in the global environment and also when nested in another function?

I have found several SO posts related to checking for the existence of a given column or columns, but they all seem to assume either that the column name will be passed as a string or the call to check existence is not nested (or both). That is not the case here.

Limey
  • 10,234
  • 2
  • 12
  • 32
  • 1
    Incidentally, what's the purpose of the `tryCatch`? I would remove that, it will silently produce the wrong result in case the user passes a bogus argument. – Konrad Rudolph Nov 28 '22 at 16:53
  • 1
    @KonradRudolph Similar to my comment to MrFlick, in an earlier iteration it was necessary to prevent an error when the function was passed a non-existent column. But you're correct: it's not necessary with this version. – Limey Nov 28 '22 at 16:58

1 Answers1

6

You want to pass though the original symbol in your outerFunction. Use

outerFunction <- function(d, col) {
  d %>% columnExists( {{col}} )
}

The "embrace" syntax will prevent early evaluation.

MrFlick
  • 195,160
  • 17
  • 277
  • 295
  • 1
    I could have sworn I'd tried that! But I went through so many conbinations of that and various others, it looks like I missed the obvious. Thank you. – Limey Nov 28 '22 at 16:56
  • @MrFlick. You mean: `col` will be early evaluated by `enquo()`?! – TarJae Nov 28 '22 at 17:11
  • 2
    @TarJae. More like `enquo` will just see `col`, rather than the value that's passed to `col`. So i'm not really using "evaluation" here correctly but I'm not sure what I could call it. – MrFlick Nov 28 '22 at 17:58