Question

I really think I'm missing something totally obvious here, but I just can't put my finger on it: how can I combine rapply() and either as.list() or eapply() or both in order to recursively convert a nested environment into a nested list.

What I'm actually after is something like as.list(<env>, recursive=TRUE) and I thought that rapply() would somehow be usefull in this.

That's the nested environment:

env <- new.env()
env$world <- new.env()
env$world$europe <- new.env()
env$world$europe$germany <- new.env()
env$world$europe$germany$foo <- 1
env$world$europe$germany$bar <- 2

That's the structrue I'd like to end up with:

env.as.list <- list(
    world=list(europe=list(germany=list(foo=1, bar=2)))
)

> env.as.list
$world
$world$europe
$world$europe$germany
$world$europe$germany$foo
[1] 1

$world$europe$germany$bar
[1] 2

And rather than building some slow recursive function based on lapply(), I would like to make use of the fast .Internal() function in rapply().

But as rapply is meant to work on lists, of course it will "stop" once it hits an environment:

> rapply(as.list(env), as.list, how="list")
$world
$world$europe
<environment: 0x000000001748d640>

What's the trick here?

Was it helpful?

Solution

Custom function:

rapply is only for nested lists; use recursion.

nested_env_list <- function(env) {
  out <- as.list(env)
  lapply(out, function(x) if (is.environment(x)) nested_env_list(x) else x)
}

Example:

 > env <- new.env(); env$a <- new.env(); env$a$b <- 1; env$a$c <- 2; env$d <- 4
 > dput(nested_env_list(env))
 structure(list(a = structure(list(b = 1, c = 2), .Names = c("b", "c")), d = 4), .Names = c("a", "d"))

Replace built-in as.list:

You can also replace the behavior of as.list to ensure this happens throughout whenever you pass an environment to as.list.

as.list.environment <- function(env) {
  out <- base::as.list.environment(env)
  lapply(out, function(x) if (is.environment(x)) as.list(x) else x)
}

Then R will figure it out automatically for all future environments. If you want the old behavior in a few select places, use base::as.list.environment explicitly. Example:

 > env <- new.env(); env$a <- new.env(); env$a$b <- 1; env$a$c <- 2; env$d <- 4
 > dput(as.list(env))
 structure(list(a = structure(list(b = 1, c = 2), .Names = c("b", "c")), d = 4), .Names = c("a", "d"))

Finally, you should ask yourself why you get infinite recursion if you replace out <- base::as.list.environment(env) above with out <- base::as.list(env).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top