LulY
LulY

Reputation: 1304

R: How to check whether object exists inside function?

Question

I need to check whether an object exists inside a function and not elsewhere. How can I do this?

To make clear what I mean I use exists:

fun <- function(){exists("x")}
x <- 1
fun()
> TRUE

My expected output for the code above is FALSE because the variable x was created outside the function. So x is saved inside the global environment and that is why the funtion returns TRUE but can we somehow test whether x is in "function environment"?


I've tried

I found this which also does not work in my case. There they suggest using

library(rlang)

f1 <- function(x){ 
  arg <- quo_name(enquo(x))
  if(exists(arg, where = .GlobalEnv)){
    return(2*x)
  } else {
    cat('variable ', '`', arg, '`', ' does not exist', sep = "")
  }
}

I expect the output being variable x does not exist for both following cases but this is not the case if x was defined outside the function before:

x <- 1
f1(x)
> 2

rm(x)
f1(x)
variable `x` does not exist

Upvotes: 3

Views: 1147

Answers (2)

divibisan
divibisan

Reputation: 12165

We can do this by specifying a specific environment for exists to search in and tell it to only look there, and not in the enclosing environments.

The where= argument tells exists where to look for that object. We can either specify it explicitly with environment() (which returns the current environment), or use the default value of -1 which drops the R_GlobalEnv from the list of environments to search.

Either way, the key is to set inherits=FALSE to limit it to only the specified environment. Otherwise, it also looks in the enclosing environments (like R_GlobalEnv) which we don't want:

x <- 1
fun <- function(){exists("x", inherits = F)}

fun()
[1] FALSE

However if we define x in the enviroment of the function, it returns TRUE:

fun <- function(){
    x<-3;
    exists("x", inherits = F)}
fun()
[1] TRUE

The example mentioned with explicitly defined environment:

fun <- function(){exists("x", where = environment(), inherits = F)}

What's happening with the default where=-1 argument? The documentation says that if you provide it with an integer, it selects the environment based on "the position in the search list". We can see that .GlobalEnv is at position 1, followed by attached packages

rm(list=ls()) # clear .GlobalEnv
x <- 3  # Add x to .GlobalEnv
ls()    # Show that x is the only thing in .GlobalEnv
[1] "x"

search() # Show the search list
 [1] ".GlobalEnv"        "package:lubridate" "package:forcats"   "package:stringr"  
 [5] "package:dplyr"     "package:purrr"     "package:readr"     "package:tidyr"    
 [9] "package:tibble"    "package:ggplot2"   "package:tidyverse" "tools:rstudio"    
[13] "package:stats"     "package:graphics"  "package:grDevices" "package:utils"    
[17] "package:datasets"  "package:methods"   "Autoloads"         "package:base"     

Now we run this function which checks for different objects in different environments by integer value:

fun <- function(){
    y <- 3
    k <- function(){
        z <- 3
        print(exists('z', -1, inherit=FALSE))
        print(exists('x', 1, inherit=FALSE))
        print(exists('y', parent.frame(), inherit=FALSE))}
    k()
    print(exists('x', -1, inherit=FALSE))
    print(exists('y', -1, inherit=FALSE))
    print(exists('x', 1, inherit=FALSE))
    print(exists('ymd', 2, inherit=FALSE))
    print(exists('last2', 3, inherit=FALSE))
    print(exists('str_detect', 4, inherit=FALSE))
}

> fun()
[1] TRUE   # Inside k(), -1 is the function env for k() - z is there
[1] TRUE   # 1 is .GlobalEnv, x is there
[1] TRUE   # to choose parent env with y, we need to specify it with parent.frame()
[1] FALSE  # -1 is the function env, x not in function env
[1] TRUE   # -1 is the function env, y is in function env
[1] TRUE   # 1 is .GlobalEnv, x is in .GlobalEnv
[1] TRUE   # position 2 is the lubridate package
[1] TRUE   # position 3 is the forcats package
[1] TRUE   # position 4 is the stringr package

From this we can see that -1 is always the local environment of the current function, while 1 is .GlobalEnv and higher numbers are attached packages as listed by search(). If you want to specify with more detail, for instance to look in fun() from within k(), then you need to specify the environment explicitly, either with a relative function like parent.frame() as above or by getting the environment as an object and referring directly as below:

fun <- function(){
    y <- 3
    env <- environment()
    k <- function(e){
        z <- 3
        print(exists('y', inherit=FALSE))
        print(exists('y', where=e, inherit=FALSE))
        }
    k(env)
}
fun()
[1] FALSE
[1] TRUE

Upvotes: 8

Allan Cameron
Allan Cameron

Reputation: 174616

Another option is to use ls:

f1 <- function() { "x" %in% ls() }

f2 <- function() { x <- 1; "x" %in% ls() }

x <- 1

f1()
#> [1] FALSE
f2()
#> [1] TRUE

Created on 2022-02-04 by the reprex package (v2.0.1)

Upvotes: 3

Related Questions