Ali Khosro
Ali Khosro

Reputation: 1830

How to subset an environment by its variable names in r

I would like to subset an environment by its variable names.

e <- new.env(parent=emptyenv())
e$a <- 1
e$b <- 2
e$d <- 3
e[ls(e) %in% c("a","b", "c")]
### if e was a list, this would return the subset list(a=1, b=2)

I could not figure out how to subset elements of an environment by their names. Using lapply or eapply does not work either. What is the proper or easy way to subset an environment by its variable names? Thank you.

Upvotes: 4

Views: 2553

Answers (5)

lead-y
lead-y

Reputation: 66

You can use rlang::env_get_list() to get a list of the bindings:

rlang::env_get_list(env=e, c("a","b"))

#$a
#[1] 1
#
#$b
#[1] 2

If you're trying to get an environment, rather than a list, I'm not sure how you would do that, other than just creating a new environment using the output of rlang::env_get_list().

If you want to include elements in your list that might not exist in the environment (like "c"), you have to specify a default value - otherwise you'll get an error:

env_get_list(env = e, c("a","b","c"))
#Error in env_get_list(env = e, c("a", "b", "c")) : argument "default" is missing, with no default

env_get_list(env = e, c("a","b","c"),default=NULL)
#$a
#[1] 1
#
#$b
#[1] 2
#
#$c
#NULL

I assume you don't want c at all, so I'd do something like:

temp <- c("a","b","c")[c("a","b","c") %in% env_names(e)]
temp
[1] "a" "b"

env_get_list(env=e,temp)
#$a
#[1] 1
#
#$b
#[1] 2

Upvotes: 2

Martin Morgan
Martin Morgan

Reputation: 46876

The [ operator usually returns the same type of object as the original, so I guess you're expecting an environment, rather than a list. The same environment but with a different set of elements, or a new environment with the specified elements? Either way I think you'll end up iterating, e.g.,

f = new.env(parent=emptyenv())
for (elt in c("a", "b"))
    f[[elt]] = e[[elt]]

Working with environments is not very idiomatic R code, which might explain why there is not a more elegant solution.

Upvotes: 2

Zheyuan Li
Zheyuan Li

Reputation: 73385

My original solution is to use get() / mget() (maybe OP saw my deleted comment earlier). Then I noticed that OP had tried eapply(), so I thought about possible solutions with that. Here it is (with help of @thelatemail).

# try some different data type
e <- new.env(parent=emptyenv())
e$a <- 1:3
e$b <- matrix(1:4, 2)
e$c <- data.frame(x=letters[1:2],y=LETTERS[1:2])

You can use either of the following to collect objects in environment e into a list:

elst <- eapply(e, "[")  ## my idea
elst <- eapply(e, identity)  ## thanks to @thelatemail
elst <- as.list.environment(e)  ## thanks to @thelatemail

#$a
#[1] 1 2 3

#$b
#     [,1] [,2]
#[1,]    1    3
#[2,]    2    4

#$c
#  x y
#1 a A
#2 b B

The as.list.environment() can be seen as the inverse operation of list2env(). It is mentioned in the "See Also" part of ?list2env.

The result elst is just an ordinary list. There are various way to subset this list. For example:

elst[names(elst) %in% c("a","b")]  ## no need to use "ls(e)" now

#$a
#[1] 1 2 3

#$b
#     [,1] [,2]
#[1,]    1    3
#[2,]    2    4

Upvotes: 3

Hong Ooi
Hong Ooi

Reputation: 57696

mget(ls(e)[ls(e) %in% c('a','b','d')], e)

Upvotes: 2

thelatemail
thelatemail

Reputation: 93938

Okay, after thinking this through a bit more, may I suggest:

mget(c("a","b"), envir=e)
#$a
#[1] 1
#
#$b
#[1] 2

Upvotes: 7

Related Questions