Home > Blockchain >  Updating a list object in the global environment from within a function in R
Updating a list object in the global environment from within a function in R

Time:11-03

I am trying to update a global list from inside a function.

Here is the code that does not work (can be sourced as a whole file):

require(rlang)
(my_list <- list(a = 1, b = "two", c = "set outside"))

print( paste("my_list$c is" , my_list$c) )

my_function <- function(x = 1, y = 2, parent_object_name = "my_list") {
  z <- x   y # do some stuff (irrelevant here)

  some_names <- "updated inside"
  upper_env_object_name <- paste0(parent_object_name, "$c")
  # browser()
  # env_poke(env = env_tail(), upper_env_object_name, some_names) # does not work
  # env_poke(env =  env_parents()[[1]], upper_env_object_name, some_names) # does not work
  
  env_poke(env = caller_env(), upper_env_object_name, some_names ) # creates `my_list$c` character vector
  # force(env_poke(env = caller_env(), upper_env_object_name, some_names )) # creates `my_list$c` character vector
  # browser()
  # env_poke(env = caller_env(), paste0("as.list(",upper_env_object_name,")"), some_names) # creates as.list(my_list$c)` character vector

  return(z)
}

my_function(x = 1, y = 2, parent_object_name = "my_list")

print(class(`my_list$c`))
print( `my_list$c`) 

print( paste("my_list$c is" , my_list$c) )

I found this but it does not help: Updating a nested list object in the global environment from within a function in R

Tried also with assign, and specifying the environment.

Background: I have some S3- subclases and want to keep track of them in the parent class object, which is also a list. The subclass objects are created "on-demand" and I want to have an overview what was created. My workaround for now is to create a new vector in the global environment and update it with :

if (exists("global_names_list")) global_names_list <<- unique(rbind(global_names_list, some_names)) else global_names_list <<- some_names

CodePudding user response:

Modify List in Global Environment

Subscript the environment like this:

f <- function(listname = "my_list", envir = .GlobalEnv) {
  envir[[listname]]$c <- "some value"
}

# test
my_list <- list(a = 1, b = "two", c = "set outside")
f()
str(my_list)
## List of 3
##  $ a: num 1
##  $ b: chr "two"
##  $ c: chr "some value"

Functional Approach

Note that working via side effects such as the code above is not the usual style used in R. Rather an object oriented style using Reference Classes or other object oriented framework or a functional style is more common. For the functional style here is an example:

g <- function(x) modifyList(x, list(c = "somevalue"))

# test
my_list <- list(a = 1, b = "two", c = "set outside")
my_list <- g(my_list)
str(my_list)
## List of 3
## $ a: num 1
## $ b: chr "two"
## $ c: chr "somevalue"

Example of object oriented processing

Regarding the background paragraph at the end of the answer here is an example of where we have a top object that contains properties a and last (both numeric) and a method add. There are any number of sub-objects that have property 'b' and inherit the add method to add a in top to b in the current object. last is the value of the last sum that was calculated by any sub-object.

library(proto)

top <- proto(a = 1, 
             last = NULL, 
             add = function(.) { top$last <- .$a   .$b; .$last }
)

sub1 <- top$proto(b = 2)
sub1$add()
# [1] 3
top$last
# [1] 3

sub2 <- top$proto(b = 3)
sub2$add()
# [1] 4
top$last
# [1] 4

CodePudding user response:

This is probably not the best way, but it is a way:

my_list <- list(a = 1, b = "two", c = "set outside")

my_function <- function(x = 1, y = 2, parent_object_name = "my_list"){
  z <- x y
  
  some_names <- "updated inside"
  lst <- get(parent_object_name)
  lst$c <- some_names
  assign(parent_object_name, lst, envir = .GlobalEnv)
  
  return(z)
  
}

my_function()
#> [1] 3

#check
my_list
#> $a
#> [1] 1
#> 
#> $b
#> [1] "two"
#> 
#> $c
#> [1] "updated inside"
  • Related