149

I would like to add elements to a list in a loop (I don't know exactly how many)

Like this:

l <- list();
while(...)
   l <- new_element(...);

At the end, l[1] would be my first element, l[2] my second and so on.

Do you know how to proceed?

user20650
  • 24,654
  • 5
  • 56
  • 91
Philippe Remy
  • 2,967
  • 4
  • 25
  • 39

2 Answers2

220

You should not add to your list using c inside the loop, because that can result in very very slow code. Basically when you do c(l, new_element), the whole contents of the list are copied. Instead of that, you need to access the elements of the list by index. If you know how long your list is going to be, it's best to initialise it to this size using l <- vector("list", N). If you don't you can initialise it to have length equal to some large number (e.g if you have an upper bound on the number of iterations) and then just pick the non-NULL elements after the loop has finished. Anyway, the basic point is that you should have an index to keep track of the list element and add using that eg

i <- 1
while(...) {
    l[[i]] <- new_element
    i <- i + 1
}

For more info have a look at Patrick Burns' The R Inferno (Chapter 2).

konvas
  • 14,126
  • 2
  • 40
  • 46
  • 14
    this is pretty painful/wordy compared to a `python` or `scala` `for() yield` generator. Will continue to hope/look for some R construct that leaves less of the work to me.. – WestCoastProjects Jun 24 '18 at 22:19
  • @javadba I'm not sure what you mean. If you replace the `while` loop in the question with a `for` you get a `for` loop which resembles a python `for` loop. If you used a `while` loop in python you would also need to call and increment `i` explicitly. If you are specifically referring to python _generators_, base R does not offer this functionality (the `iterators` package has similar functions but does not quite work in the same way). – konvas Sep 13 '18 at 09:03
  • I did say `generators` in the comment. A construct that *creates* the iteration variable and auto-increments would be "nice" instead of the above - which although possible in the other languages would not be preferred. Will look at `iterators` – WestCoastProjects Sep 13 '18 at 13:29
  • @javadba There is a construct that lets you initialise the variable and auto-increment, it's the for loop, e.g. `for (i in 1:10) print(i)`, or perhaps I have misunderstood the question. – konvas Sep 13 '18 at 14:02
  • yea that's what I was looking for: that and more are described here https://www3.nd.edu/~steve/computing_with_data/21_looping/looping_iterators_part1.html – WestCoastProjects Sep 13 '18 at 14:04
  • @javadba I think when you do `iter(1:10)` the whole vector `1:10` is created as opposed to python generators which don't call the expression from before – konvas Sep 13 '18 at 14:16
  • Yea that would be true - but it is uncommon that my needs are for the "lazy" approach so eager creation of the whole vector is fine. – WestCoastProjects Sep 13 '18 at 14:18
  • regarding adding to the front of a list -- seems like a general rule is to try c(list(),mylist) to concat to the front of a list: ```mylist <- list(c('a','b'),c('b','c')); mylist2 <- c(c('c','d'),mylist) # will have 4 elements; mylist3 <- c(list(c('c','d')),mylist) # will have 3 elements - probably is usually wanted``` – Richard DiSalvo May 06 '19 at 05:47
  • every time you build a new list a copy is created and this is very inefficient. You can always select elements of a list in different order by indexing. For example `list('a', 'b', 'c')[c(2, 1, 3)]`. – konvas Oct 17 '20 at 14:49
8

The following adds elements to a vector in a loop.

l<-c()
i=1

while(i<100) {
    
    b<-i
    l<-c(l,b)
    i=i+1
}
jochen
  • 3,728
  • 2
  • 39
  • 49
Jason
  • 1,559
  • 1
  • 9
  • 14
  • 15
    What's the purpose of `b <- i` in the loop? – talat Oct 22 '14 at 13:35
  • Good point. I wanted to show more of where OP would put the element to be added to the list rather than just use 'i' – Jason Oct 22 '14 at 13:37
  • 39
    Where is the list in your code? (i.e. c() is no list). – ddiez Oct 22 '14 at 13:57
  • 4
    @DavidArenburg out of curiosity -- is there a better way to do this when you don't know the size of the list beforehand? – Berk U. Feb 16 '16 at 17:17
  • 1
    @BerkU. You don't need to know the size of the list beforehand but you need an upper bound to it (which in most cases is straightforward to figure out). If your upper bound is N, then `x <- vector("list", N); i <- 1; while(...) { do something; x[[i]] <- something; i <- i + 1}` will add to the list some number of elements, leaving the others as NULL. Then it depends on what format you want your results in, e.g. if it's a vector you can run `do.call(c, x)`, or you can simply get rid of the NULL entries and keep your results in a list. – konvas Aug 12 '16 at 13:39
  • 4
    `c()` will merge dataframes. Instead of list of dataframes you will get one huge dataframe – user1700890 Mar 23 '17 at 15:31
  • 8
    c() produces a vector, not a list: this won’t handle many elements. It’s also inefficient for memory to build up a vector as you go if you know how many elements you’ll fill in. – Tom Kelly Jun 03 '18 at 13:13
  • 2
    this seems to be an answer about a vector , rather than a list() – baxx Mar 19 '19 at 22:53