3

Consider

a <- b <- 1:3
if(sample(2, 1) == 1) a[2] <- 5 else b[2] <- 5

This is repetitive. I would not like to have to write <- 5 twice. It would be ideal to use

a <- b <- 1:3
assign(if(sample(2, 1) == 1) "a[2]" else "b[2]", 5)

but assign is no good for sub-assignment. What idiomatic alternatives exist?

J. Mini
  • 1,868
  • 1
  • 9
  • 38

3 Answers3

3

I don't think there's an idiomatic way to do this, but it is certainly possible to do it with a single assignment:

a <- b <- 1:3
s <- if(sample(2, 1) == 1) "a" else "b"
assign(s, `[<-`(get(s), 2, 5))

a
#> [1] 1 2 3

b
#> [1] 1 5 3

Created on 2021-09-26 by the reprex package (v2.0.0)

Allan Cameron
  • 147,086
  • 7
  • 49
  • 87
3

I think the idiomatic way to do it is not to have two variables, let them be entries in a list:

l <- list()
l$a <- l$b <- 1:3
var <- sample(c("a", "b"), 1)
l[[var]][2] <- 5
l
#> $b
#> [1] 1 2 3
#> 
#> $a
#> [1] 1 5 3

Created on 2021-09-26 by the reprex package (v2.0.0)

You could get something closer to what you were asking for by fooling around with environments:

a <- b <- 1:3
var <- sample(c("a", "b"), 1)
e <- environment()
e[[var]][2] <- 5

Created on 2021-09-26 by the reprex package (v2.0.0)

but I wouldn't call this idiomatic.

user2554330
  • 37,248
  • 4
  • 43
  • 90
  • Indeed. Related reading: [Why is using assign bad?](https://stackoverflow.com/questions/17559390/why-is-using-assign-bad); [FAQ 7.21](https://cran.r-project.org/doc/FAQ/R-FAQ.html#How-can-I-turn-a-string-into-a-variable_003f) ("At least in the first two cases it is often easier to just use a list"); `fortune(236)`; `fortune(106)`; `fortune(181)`. – Henrik Sep 26 '21 at 09:59
2

You could use an eval(parse()) approach.

eval(parse(text=paste(c('a', 'b')[(sample(2, 1) == 1) + 1], '[2] <- 2')))

Or short:

eval(parse(text=paste(sample(c('a', 'b'), 1), '[2] <- 2')))
jay.sf
  • 60,139
  • 8
  • 53
  • 110