Verify Object Existence Inside a Function in R

Verify object existence inside a function in R

You should have checked ?exists:

Usage:

exists(x, where = -1, envir = , frame, mode = "any",
inherits = TRUE)

Arguments:

x: a variable name (given as a character string).

Do exists("y")

R: How to check whether object exists inside function?

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

R - Inside function check if object exists

The R interpreter evaluates first the line f(x) and then what is in the function. The interpreter finds an unknown element x and stops immediately evaluating the rest of the code.

Therefore, it won't work in any scenario you've given, as the problem comes before the function evaluation.

You have to put your check outside the function:

if(exists("x")) {
f(x)
}

Or, depending on your needs, you can do:

f <- function(x) {
if(!missing("x")) { return(x * 2) }
}

f() // do nothing
f(2) // return 4

How to check if object (variable) is defined in R?

You want exists():

R> exists("somethingUnknown")
[1] FALSE
R> somethingUnknown <- 42
R> exists("somethingUnknown")
[1] TRUE
R>

R - checking for existence of objects in a function

You can specify in which environment you are looking first:

test <- function(a,b) { 
print(exists(as.character(substitute(a)), envir=parent.frame()))
if (!exists(as.character(substitute(a)), envir=parent.frame())) {a <- 0}
ifelse(a > b, "hello", "world")
}

This way:

y <- 10
test(x,y)

# [1] FALSE
# [1] "world"

x <- 11
test(x,y)

#[1] TRUE
#[1] "hello"

rm(list=ls())

test <- function(a,b) {
print(exists(as.character(substitute(a)), envir=parent.frame()))
if (!exists(as.character(substitute(a)), envir=parent.frame())) {a <- 0}
ifelse(a > b, "hello", "world")
}
blah <- function() { x <- 11; y <- 10; test(x,y)}
blah()

#[1] TRUE
#[1] "hello"

How can I write a function to detect whether a object exists in R and if it doesn't, read in a file by that name?

First, both exists and assign take an argument envir=, and it is relevant. While I generally dislike the use of assign in most cases, I think it can be done safely using parent.frame() so that this is safe and doesn't work too far out of data-scope.

getit <- function(x, envir = parent.frame()) {
varname <- as.character(substitute(x))
if (!exists(varname, envir = envir)) {
dat <- readr::read_table2(varname, col_names = FALSE)
assign(varname, dat, envir = envir)
}
get(varname, envir = envir)
}

Some sample data:

write.table(iris[1:3,], "iris.tab", col.names = FALSE, row.names = FALSE)
list.files()
# [1] "iris.tab" "question.R"

And its execution:

ls()                       # proof that the data does not yet exist
# [1] "getit"
getit(iris.tab)
# Parsed with column specification:
# cols(
# X1 = col_double(),
# X2 = col_double(),
# X3 = col_double(),
# X4 = col_double(),
# X5 = col_character()
# )
# # A tibble: 3 x 5
# X1 X2 X3 X4 X5
# <dbl> <dbl> <dbl> <dbl> <chr>
# 1 5.1 3.5 1.4 0.2 "\"setosa\""
# 2 4.9 3 1.4 0.2 "\"setosa\""
# 3 4.7 3.2 1.3 0.2 "\"setosa\""
ls()
# [1] "getit" "iris.tab"

It prefers the local copy, not reading from the file if the object exists:

iris.tab[1,1] <- 999
iris.tab
# # A tibble: 3 x 5
# X1 X2 X3 X4 X5
# <dbl> <dbl> <dbl> <dbl> <chr>
# 1 999 3.5 1.4 0.2 "\"setosa\""
# 2 4.9 3 1.4 0.2 "\"setosa\""
# 3 4.7 3.2 1.3 0.2 "\"setosa\""
getit(iris.tab)
# # A tibble: 3 x 5
# X1 X2 X3 X4 X5
# <dbl> <dbl> <dbl> <dbl> <chr>
# 1 999 3.5 1.4 0.2 "\"setosa\""
# 2 4.9 3 1.4 0.2 "\"setosa\""
# 3 4.7 3.2 1.3 0.2 "\"setosa\""
readLines("iris.tab")[1] # file is unchanged
# [1] "5.1 3.5 1.4 0.2 \"setosa\""

It works even if the file does not exist but the data does:

mt <- mtcars
head(getit(mt))
# mpg cyl disp hp drat wt qsec vs am gear carb
# Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
# Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
# Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
# Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
# Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
# Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
ls()
# [1] "getit" "iris.tab" "mt"
list.files()
# [1] "iris.tab" "question.R"

and clearly fails when the object and the data does not exist:

getit(foo.csv)
# Error: 'foo.csv' does not exist in current working directory ('C:/Users/r2/StackOverflow/15997184/67753553').

(You might choose to try to catch that error and elicit something a little more contextual, such as "neither the file nor the object exist ...", over to you.)

Condition on the existence of the object in the User-defined function, not in the Global environment in R

There are the "where" and "envir" arguments to the exists function, see ?exists.

f <- function(x){
if(is.numeric(x)) y <- x + 2
exists(x = "y", where = -1)
}
f("a")
[1] FALSE
f(1)
[1] TRUE

Alternatively, using the envir argument:

f <- function(x){
if(is.numeric(x)) y <- x + 2
exists(x = "y", envir = rlang::current_env())
}

However, in practical cases it might be preferable to make a NULL variable instead, often paired with a user defined null coalescing operator (%||%) to specify a default value later.

f <- function(x){
y <- if(is.numeric(x)) x + 2 else NULL
!is.null(y)
}
# same as
f <- function(x){
y <- if(is.numeric(x)) x + 2
!is.null(y)
}

In R, how can I check for the existence of a function in an unloaded package?

You can use the exists function to look inside namespaces:

exists2 <- function(x) {

assertthat::assert_that(assertthat::is.string(x))

split <- base::strsplit(x, "::")[[1]]

if (length(split) == 1) {
base::exists(split[1])
} else if (length(split) == 2) {
base::exists(split[2], envir = base::asNamespace(split[1]))
} else {
stop(paste0("exists2 cannot handle ", x))
}
}


Related Topics



Leave a reply



Submit