Reputation: 1304
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
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
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