rlist

How to unlist a list of lists and give two outputs in R


I have a list of lists called mylist. In the following example, mylist has two lists, in which contains another two lists of random numbers.

mylist <- list()
list1 <- rnorm(100)
list2 <- rnorm(100)
list3 <- rnorm(100)
list4 <- rnorm(100)

mylist[[1]] <- list("list1" = list1, "list2" = list2)
mylist[[2]] <- list("list1" = list3, "list2" = list4)  

When printing the mylist, it should give us something like

[[1]]
[[1]]$list1
  [1]  0.0382233645  0.2416676908  1.1910173996  0.7833025597 -0.7144370041
  [6]  1.9762077872  0.9438593806  0.1590264664 -0.5323112247  1.2820246358...
[[1]]$list2
  [1] -0.084982839 -0.462448106  0.771458598  1.354049503 -1.616381590 -0.368789648
  [7] -0.678521566 -0.340799431 -0.441370037 -0.212117105 -0.921661840  0.893550382...
[[2]]
[[2]]$list1
  [1]  0.367971534  0.028976364  1.391761655 -0.033661646  1.196138270 -1.645738949
  [7]  0.086037770  0.530143788  0.639389906  2.193683933 -1.427285285  0.430282258...
[[2]]$list2
  [1]  0.235027959  2.110340896 -0.951831561 -1.510063739 -0.007402732 -0.575736079
  [7] -0.363731268  1.232404358 -0.848716435  1.217101680 -0.014577878  0.440142686...

I want to unlist mylist so that the final results (res) will output two elements. The first element will combine all available list1, and the second element will aggregate all available list2. The final results should look like

res$list1
[1]  0.0382233645  0.2416676908  1.1910173996  0.7833025597 -0.7144370041
[6]  1.9762077872  0.9438593806  0.1590264664 -0.5323112247  1.2820246358...
[101]  0.367971534  0.028976364  1.391761655 -0.033661646  1.196138270 -1.645738949
[107]  0.086037770  0.530143788  0.639389906  2.193683933 -1.427285285  0.430282258...

res$list2
[1]  0.367971534  0.028976364  1.391761655 -0.033661646  1.196138270 -1.645738949
[7]  0.086037770  0.530143788  0.639389906  2.193683933 -1.427285285  0.430282258...
[101]  0.235027959  2.110340896 -0.951831561 -1.510063739 -0.007402732 -0.575736079
[107] -0.363731268  1.232404358 -0.848716435  1.217101680 -0.014577878  0.440142686...

What is the right way to achieve that in R? Thanks!


Solution

  • We can use Map to combine the sub-lists of each top-level list element.

    Reproducible data:

    set.seed(42)
    mylist <- list()
    list1 <- rnorm(5)
    list2 <- rnorm(5)
    list3 <- rnorm(5)
    list4 <- rnorm(5)
    mylist[[1]] <- list("list1" = list1, "list2" = list2)
    mylist[[2]] <- list("list1" = list3, "list2" = list4)  
    mylist
    # [[1]]
    # [[1]]$list1
    # [1]  1.3709584 -0.5646982  0.3631284  0.6328626  0.4042683
    # [[1]]$list2
    # [1] -0.10612452  1.51152200 -0.09465904  2.01842371 -0.06271410
    # [[2]]
    # [[2]]$list1
    # [1]  1.3048697  2.2866454 -1.3888607 -0.2787888 -0.1333213
    # [[2]]$list2
    # [1]  0.6359504 -0.2842529 -2.6564554 -2.4404669  1.3201133
    
    do.call(Map, args = c(list(f = c), mylist))
    # $list1
    #  [1]  1.3709584 -0.5646982  0.3631284  0.6328626  0.4042683  1.3048697  2.2866454 -1.3888607 -0.2787888 -0.1333213
    # $list2
    #  [1] -0.10612452  1.51152200 -0.09465904  2.01842371 -0.06271410  0.63595040 -0.28425292 -2.65645542 -2.44046693  1.32011335
    

    If it's always list1 and list2 (a pre-known length within mylist), then we can do away with do.call and just use Map directly:

    Map(c, mylist[[1]], mylist[[2]])
    

    The (sole?) benefit of using do.call is that if mylist is either unknown-length or long-length and you don't want to have to type in all of mylist[[1]], mylist[[2]], ..., mylist[[n]] (which would work), then a simple expression handles it all for you.