2

Suppose we would like to store pairs as elements in a list, e.g.,

> x <- list()
> x[[1]] <- list(1,2)
> x[[2]] <- list(3,4)
> x
[[1]]
[[1]][[1]]
[1] 1

[[1]][[2]]
[1] 2


[[2]]
[[2]][[1]]
[1] 3

[[2]][[2]]
[1] 4

If one wants to create the same thing with concatenation, without explicit reference to an index of the outer list, a natural approach (see EDIT below for motivation for this approach) is

> c(list(list(1,2)),list(3,4))
[[1]]
[[1]][[1]]
[1] 1

[[1]][[2]]
[1] 2


[[2]]
[1] 3

[[3]]
[1] 4

Is there a way to avoid the "flattening" of the second argument to c() and thereby use concatenation to produce x?

EDIT: I should have made my intended application clear.

In the end, I would like to add pairs to a list whenever I find one as I go through a loop. Currently I maintain an index for the main list to add new pairs to the end of it when one is found, however using c is perhaps more natural. So using an explicit index the loop may look like this

index <- 1
l <- list()
for (i in 1:10) {
    for (j in (i+1):10) {
        if ( (i+j)%%2 == 0 ) {
            l[[index]] <- list(i,j)
            index <- index + 1
        }
    }
}

And the failed attempt to use c looks like this

l <- list()
for (i in 1:10) {
    for (j in (i+1):10) {
        if ( (i+j)%%2 == 0 )
            l <- c(l,list(i,j))
    }
}

ANSWER: as noted by Tommy below, an extra list() is needed, so the following works:

l <- list()
for (i in 1:10) {
    for (j in (i+1):10) {
        if ( (i+j)%%2 == 0 )
            l <- c(l,list(list(i,j)))
    }
}

Kind of obvious thing I should have figured out. Tommy also notes that this might not be a very good way of doing things.

Rahul Savani
  • 872
  • 1
  • 10
  • 24
  • not sure what you are looking for, but is it `c(list(1, 2), list(3, 4))` ? – Ramnath Dec 19 '11 at 04:53
  • 1
    As a general comment, if your actual problem resembles your example code, it is very unlikely that appending things to the end of lists is the right approach (which, as you've discovered, if awkward in R). When you encounter a simple task that feels awkward, that's a good sign you're not solving your problem in the way the language is intended to be used. – joran Dec 19 '11 at 05:41
  • 1
    glad you got your immediate problem figured out and I'll reiterate what I said below re: preallocation. This answer shows some performance comparisons...which can easily balloon up to minutes or hours with more complicated functions. Good luck! http://stackoverflow.com/questions/4034059/iteratively-constructed-dataframe-in-rx – Chase Dec 19 '11 at 05:44
  • @joran,@Chase: I was just knocking something up quickly and so the poor design didn't matter in this instance. However, I take your general point, and then a question remains: If one doesn't have a reasonable upper bound on the size of the data structure, e.g., list, that one will end up with, what would you do? Just allocate something and then let it dynamically grow if required? – Rahul Savani Dec 19 '11 at 05:50
  • I can't find it at the moment, but I recall someone (Ripley?) writing at one point that it can actually be faster to preallocate with a rather wild overestimate of the size (big enough, obviously, and that runs into problems as well). I realize that doesn't completely answer your question, but it's all I got at the moment. – joran Dec 19 '11 at 05:59

1 Answers1

3

I guess the simple answer is "no, you need the extra list() wrapper".

...but in general, it is better (faster/less memory) to add values using indexing to a pre-allocated list than using c()

x <- NULL
for(i in 1:2) x <- c(x, list(list(i,i+1)))
x

x <- vector('list', 2)
for(i in seq_along(x)) x[[i]] <- list(i,i+1)
x

And if lapply can be used, that is usually even more efficient:

lapply(1:2, function(i) list(i, i+1))
Tommy
  • 39,997
  • 12
  • 90
  • 85