Home > front end >  assigning value to an object when referencing it by a string within a for loop
assigning value to an object when referencing it by a string within a for loop

Time:11-10

I often struggle with understanding how to assign values in R within loops. The desired behavior seems simple to me, but I clearly don't have a good grasp on the subtleties of evaluation and assignment in R.

For example, I've got a bunch of data objects that I want to add a comment to each object (they are unrelated and so using them together in a list beyond this assignment does not make sense). Here's a MWE


my_comment <- paste0("these objects were created on ", date())

obj1 <- "content1"
obj2 <- "content2"

obj_l <- list(obj1, obj2)

for(obj in obj_l) {
comment(obj) <- my_comment
}

## get 'NULL', but want "these objects were created ..."
comment(obj1)

## get 'NULL'
comment(obj_l)

## assignment is only made to temp variable 'obj'
## This makes sense, but not the desired outcome. 
comment(obj)

I imagine the solution will look something like the following pseudo code

obj_l <- c("obj1", "obj2")

for(name in obj_l)
 unknown_function(name, comment, my_comment, unknown_args)

}

or


modify(obj_l, my_comment, unknown_syntax)

If my pseudo code is on track, can someone help me with the unknown_ parts?

CodePudding user response:

Any of these work to comment the components of obj_l or in the case of #3 to produce a new list with comments added. We show that the object has been changed for #1 but the others are the same.

# 1
obj1 <- "content1"
obj2 <- "content2"
obj_l <- list(obj1, obj2)
for(i in seq_along(obj_l)) {
  comment(obj_l[[i]]) <- "some comment"
}
dput(obj_l)
## list(structure("content1", comment = "some comment"), 
##   structure("content2", comment = "some comment"))


# 2
obj1 <- "content1"
obj2 <- "content2"
obj_l <- mget(c("obj1", "obj2"))
for(nm in names(obj_l)) {
  comment(obj_l[[nm]]) <- "some comment"
}

# 3
obj1 <- "content1"
obj2 <- "content2"
obj_l <- list(obj1, obj2)
obj_2 <- lapply(obj_l, `comment<-`, "some comment")

If what you wanted was to comment obj1 and obj2 then

# 4
obj1 <- "content1"
obj2 <- "content2"
nms <- c("obj1", "obj2")
e <- .GlobalEnv # environment with objects
for(nm in nms) comment(e[[nm]]) <- "some comment"

# 5
obj1 <- "content1"
obj2 <- "content2"
obj_l <- mget(c("obj1", "obj2")) # named list w obj1, obj2 elements
lapply(obj_l, `comment<-`, "some comment") |> list2env(.GlobalEnv)

CodePudding user response:

With lists in a loop, it's usually easier to loop over the indexes or the names, not the actual objects:

for(i in seq_along(obj_l)) {
  comment(obj_l[[i]]) <- my_comment
}

comment(obj_l[[i]])
# [1] "these objects were created on Wed Nov  9 10:55:27 2022"

And you have added a comment to the item in the list, don't expect obj1 to have a comment. (Better not to think about obj1 at all, what you have is obj_l which has 2 elements, obj_l[[1]] and obj_l[[2]].

CodePudding user response:

After some more reading of help pages and trial and error, here's my solution

obj1 <- "content1"
obj2 <- "content2"

obj_l <- c("obj1", "obj2")

comment <- '"my comment!"'

for(x in obj_l) {
    my_exp <- paste0("comment(", x, ") <- ", comment)
    parse(text = my_exp)
}

comment(obj1)
# [1] "my comment"

CodePudding user response:

My attempt at answering the actual question without using the comment() function.

See Below:

create_comment <- function(object, comment) {
  
  new_object <- list(Object = object, Comment = comment)
  return(new_object)
}

test <- map(c(obj1, obj2), create_comment, comment = my_comment)

Instead of using the comment() function I built a function that creates a named list object for each individual object. This makes it so that I can create a large list with all of my stored objects and it also stores the character string my_comment within the list.

the output from test shows...

[[1]]
[[1]]$Object
[1] "content1"

[[1]]$Comment
[1] "these objects were created on Wed Nov 09 16:04:04 2022"


[[2]]
[[2]]$Object
[1] "content2"

[[2]]$Comment
[1] "these objects were created on Wed Nov 09 16:04:04 2022"

I can then use [[i]] to access my objects

For example:

test[[1]]

$Object
[1] "content1"

$Comment
[1] "these objects were created on Wed Nov 09 16:04:04 2022"

And I can further access my individual comments or object by using the $

test[[1]]$Comment

[1] "these objects were created on Wed Nov 09 16:04:04 2022"
  • Related