11

I have written a function that it's output is a list. I want to put my function into a loop and I'd like to store output of each iteration (which is of course a list) into a bigger list. In other words, each element of this BIG list is also a list. c() does not do what I want. Is there any way to do that?

To understand better what I'm asking, consider the example below:

iter1 <- list(item1 = 1, item2 = "a")
iter2 <- list(item1 = 1, item2 = "b")
All <- list(iter1 = iter1, iter2 = iter2)

I want to be able to do something similar to the code above but in a loop. How can I do that?

Thanks for your help,

4
  • 1
    I think you want to say "nesting" instead of "concatenating" in the title...and maybe mention the loop thing? Commented May 17, 2013 at 6:05
  • 1
    you want c, you just need an extra call to list: c(LL, list(l1)) Commented May 17, 2013 at 6:29
  • 3
    Don't grow objects in loops!! Preallocate and then fill. Commented May 17, 2013 at 6:58
  • This is not concatenation, I've fixed title and tags. Commented Dec 15, 2015 at 11:59

4 Answers 4

10

There's another way to assign to a list, using my_list[[name or number]] <-. If you really want to do that in a loop, just looping over things with names like iter1, iter2, ...

A <- list()
n_iter <- 2
for (i in 1:n_iter){
    iname <- paste("iter",i,sep="")
    A[[iname]] <- get(iname)
}

As @mnel pointed out, dynamically growing a list is inefficient. The alternative is, I think, to use lapply:

n_iter <- 2
inames <- paste("iter",1:n_iter,sep="")
names(inames) <- inames
A <- lapply(inames,get)

This can also be done with a data frame, which would be a better format if your sublists always have two elements, each having a consistent class (item1 being numeric and item 2 being character).

n_iter <- 2
DF <- data.frame(item1=rep(0,n_iter),item2=rep("",n_iter),stringsAsFactors=FALSE)
for (i in 1:n_iter){
     iname <- paste("iter",i,sep="")
     DF[i,] <- get(iname)
     rownames(DF)[i] <- iname
}

#       item1 item2
# iter1     1     a
# iter2     1     b

However, that's a pretty ugly way of doing things; things get messy pretty quickly when using get. With your data structure, maybe you want to create iter1 and iter2 in a loop and immediately embed them into the parent list or data frame?

n_iter = 10
DF <- data.frame(item1 = rep(0,n_iter), item2 = rep("",n_iter))
for (i in 1:n_iter){
    ... do stuff to make anum and achar ...
    DF[i,"item1"] <- anum
    DF[i,"item2"] <- achar
}

Where anum and achar are the values of item1 and item2 you want to store from that iteration. Elsewhere on SO, they say that there is an alternative using the data.table package that is almost 10x as fast/efficient as this sort of data-frame assignment.

Oh, one last idea: if you want to put them in a list first, you can easily convert to a data frame later with

DF <- do.call(rbind.data.frame,A)
Sign up to request clarification or add additional context in comments.

2 Comments

@SimonO101 . Thanks for pointing out the issue with my first example. :) Please let me know if there's a problem with the edited version.
+ 1 for your comprehensive treatment of the alternatives
5

This gets you the equivalent of your All

c(iter1=list(iter1), iter2=list(iter2))

> identical(c(iter1=list(iter1), iter2=list(iter2)), All)
[1] TRUE

Let's say you'd like to add a third list to All:

c(All, list(iter3=iter3))

If you don't care for the list names, it looks a little cleaner

 c(list(iter1), list(iter2))

Comments

2

I think @Frank's answer is the correct one here, but the first example he gave seemed a little strange. I think you want to do this...

bigLL <- list()
for( i in 1:3 ){

  ll <- list( item1 = i , item2 = letters[i] )
  bigLL[[i]] <- ll

}

bigLL
#[[1]]
#[[1]]$item1
#[1] 1

#[[1]]$item2
#[1] "a"


#[[2]]
#[[2]]$item1
#[1] 2

#[[2]]$item2
#[1] "b"


#[[3]]
#[[3]]$item1
#[1] 3

#[[3]]$item2
#[1] "c"

but you should consider the alternatives by Frank if possible.

Comments

-1

This worked for me very well, hope it helps.

  data = list()

  for(i in 1:3)
  {
    tmp = c(1,2,3)
    data = rbind(data, tmp)
  }

1 Comment

rbind is not for lists - it will ignore the fact that data starts as a list. Also, continually calling rbind in a loop is slow as data must be re-evaluated at each step. Try using data[[i]] <- tmp in each loop and then do.call(rbind, data) after the loop.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.