0

Say I have vectors a1, a2, a3, and I want to iteratively edit each vector. Normally, you could do something along the lines of

for (i in 1:3) {
   assign(paste("a", i, sep=""), ...)
}

However, this would re-assign the entire objects 'a1, a2, a3`. Is there are a way to only edit a single value in each of the vectors without having to copy the entire vectors? Is there an R function that intakes a string and returns the object with that name? get() only returns the value of the object with the inputted name.

I've tried paste("a", i, sep="")[1] and get(paste("a", i, sep=""))[1], but neither of these two options return the object with name "ai".

EDIT: Here is a reproducible example.

a1 = c(1,2,3)
a2 = c(4,5,6)

for (i in 1:2) {
   assign(get(paste("a", i, sep=""))[1], 0)
}

Error in assign(get(paste("a", i, sep = ""))[1], 0) : 
  invalid first argument

My goal is to iteratively change the first element of both vectors to 0. The error arises because get(paste("a", 1, sep = "")) returns [1] 1 2 3 rather than the object a1 itself.

JLinsta
  • 109
  • 2
  • 1
    Can you please make this reproducible? Also, it sounds like you have vectors a1, a2, and a3 but you want to reference a vector called `ai`? Where did that come from? – Jon Spring Sep 01 '23 at 17:50
  • @JonSpring Sorry by `ai`, I mean `a1`, `a2`, or `a3` (depending on the iteration of the for loop. – JLinsta Sep 01 '23 at 17:57

4 Answers4

2

This seems a little unidiomatic for R. Free-floating variables that will be acted on together should usually be in a list. (See this "code smell": https://luzkan.github.io/smells/global-data).

Then you can do

a <- list(a1,a2)
a2 <- lapply(a, \(x) {x[1] <- 0; x})

which assigns zero to the first element in each item in the list and returns the updated list.

Jon Spring
  • 55,165
  • 4
  • 35
  • 53
  • This does produce the desired output, but it is possible to accomplish without copying the vectors `a1` and `a2` into a list? – JLinsta Sep 01 '23 at 18:21
  • In my specific application, I'm looking to iteratively edit matrices, so I would like to avoid copying the matrices into a list, if possible. – JLinsta Sep 01 '23 at 18:28
  • 1
    It sounds like your problem has important unspecified dimensions (e.g. multiple matrices, a concern for memory or processing time (or something else) that makes you wary of putting them in a list, etc.) which would be helpful for people to consider when suggesting potential solutions. – Jon Spring Sep 01 '23 at 18:34
  • I did specify that I was looking for a solution that avoids copying the entire vectors. – JLinsta Sep 01 '23 at 18:39
  • 2
    It is often helpful to understand the questioner's motivation for their specifications, so that we can discover as early as possible a potential XY problem, where an alternate approach might make more sense, even if it is not the approach the OP was starting with. – Jon Spring Sep 01 '23 at 18:46
1

Define the environment, e, that the vectors are located in. Here we use .GlobalEnv but environment() would have worked as well. Then use ls to get a character vector of the vector names (or use paste as in the question or just write them out as shown in the comment). Now iterating through those names use e[[nm]][1] to refer to the first element of the current one and just assign 0 to it in the ordinary way.

a1 <- 1:3; a2 <- 2 * a1

e <- .GlobalEnv
nms <- ls(pattern = "^a[12]$", e)  # or paste0("a", 1:2) or c("a1", "a2")
for(nm in nms) e[[nm]][1] <- 0

a1
## [1] 0 2 3

a2
## [1] 0 4 6
G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
0

How about putting your call to get in parentheses to ensure it is evaluated before the indexing?

a1 <- 1:3
a2 <- 4:6
for (i in 1:2) print( (get(paste("a", i, sep="")))[i] )
[1] 1
[1] 5

Though I suspect there are much better ways of doing what you want to do, but without context it's difficult to make suggestions.

As Jon Spring says, a reproducible example would have been helpful.

Limey
  • 10,234
  • 2
  • 12
  • 32
  • I've tried this, but the issue is that get("a1") returns the **value** of `a1`. Your code prints the values in the vectors, but I want to re-assign the values. Something like `(get(paste("a", i, sep="")))[1] = 4` produces a "target of assignment expands to non-language object" error. – JLinsta Sep 01 '23 at 18:00
  • 1
    Could you please expand your question to show an example of what inputs you have and what outputs you expect? – Jon Spring Sep 01 '23 at 18:04
0

I've found a solution.

a1 = c(1,2,3)
a2 = c(4,5,6)

for (i in 1:2) {
  eval(parse(text = paste("a", i, "[1] = 0", sep="")))
}
JLinsta
  • 109
  • 2
  • I'm glad that you found something that solves your immediate issue. As an FYI, there are many experienced R users who would discourage using the `eval(parse())` construct because it can introduce new problems. https://stackoverflow.com/questions/13649979/what-specifically-are-the-dangers-of-evalparse – Jon Spring Sep 01 '23 at 19:06
  • Thank you for this resource. I'll certainly spend more time looking for a more optimal answer. – JLinsta Sep 01 '23 at 19:10