user1436187
user1436187

Reputation: 3376

Unlist list of lists that have matrix elements to a list of matrices

Suppose that A, B and C are matrices. And I have a list of them like this:

  list(A,list(B,C))

I want to convert it to this:

  list(A,B,C)

The unlist function convert the matrices to vectors!

For example:

A=matrix(1:10,nrow=2)
B=list(A,list(A,A))
unlist(B)

Upvotes: 16

Views: 1817

Answers (4)

BrodieG
BrodieG

Reputation: 52657

Here is a recursive implementation:

flatten2 <- function(X) if(is.list(X)) Reduce(c, lapply(X, flatten2)) else list(X)

Then:

str(flatten2(B))   # list of three matrices:
# List of 3
#  $ : int [1:2, 1:5] 1 2 3 4 5 6 7 8 9 10
#  $ : int [1:2, 1:5] 1 2 3 4 5 6 7 8 9 10
#  $ : int [1:2, 1:5] 1 2 3 4 5 6 7 8 9 10

And more complex:

C <- list(A, list(list(A, A), A))
str(flatten2(C))
# List of 4
#  $ : int [1:2, 1:5] 1 2 3 4 5 6 7 8 9 10
#  $ : int [1:2, 1:5] 1 2 3 4 5 6 7 8 9 10
#  $ : int [1:2, 1:5] 1 2 3 4 5 6 7 8 9 10
#  $ : int [1:2, 1:5] 1 2 3 4 5 6 7 8 9 10

Also, a "wordier" but faster version (this is the one tested by Pierre):

flatten <- function(X) {
  res <- list()
  for(i in X) res <- c(res, if(is.list(i)) Recall(i) else list(i))
  res
}

You could also make flatten2 a little faster by replacing Reduce with do.call, but that is a little less cute. flatten remains the fastest even with that change.

Upvotes: 14

AndreyAkinshin
AndreyAkinshin

Reputation: 19031

You can use the list.flatten function of the rlist package. An example:

library(rlist)
A1 <- matrix(1:10, nrow = 2)
A2 <- matrix(11:20, nrow = 2)
A3 <- matrix(21:30, nrow = 2)
B <- list(A1, list(A2, A3))
C <- list.flatten(B)

C    
# [[1]]
#      [,1] [,2] [,3] [,4] [,5]
# [1,]    1    3    5    7    9
# [2,]    2    4    6    8   10
# 
# [[2]]
#      [,1] [,2] [,3] [,4] [,5]
# [1,]   11   13   15   17   19
# [2,]   12   14   16   18   20
# 
# [[3]]
#      [,1] [,2] [,3] [,4] [,5]
# [1,]   21   23   25   27   29
# [2,]   22   24   26   28   30

str(C)    
# List of 3
#  $ : int [1:2, 1:5] 1 2 3 4 5 6 7 8 9 10
#  $ : int [1:2, 1:5] 11 12 13 14 15 16 17 18 19 20
#  $ : int [1:2, 1:5] 21 22 23 24 25 26 27 28 29 30

Upvotes: 7

Pierre L
Pierre L

Reputation: 28451

So many great solutions. I wanted to contribute a comparison:

library(microbenchmark)
microbenchmark(
  flatten = flatten(lst),
  delist = delist(lst),
  list.flatten = list.flatten(lst))
#          expr     min       lq      mean   median       uq
#       flatten  14.606  16.3830  19.17356  17.7640  18.5540
#        delist 228.559 239.6115 251.52930 247.5070 254.0205
#  list.flatten  51.318  56.0545  63.87871  61.7785  70.2660
#      max neval
#   41.449   100
#  406.589   100
#  145.267   100

Data

A <- matrix(1e4, 100)
lst <- list(A, list(A, list(A, list(A, list(A, list(A, list(A)))))))
flatten <- function(X) {
  res <- list()
  for(i in X) res <- c(res, if(is.list(i)) Recall(i) else list(i))
  res
}

delist<-function(x) {
    lists <- sapply(x, class)=="list"
    while(any(lists)) {
        x<-mapply(function(y,z) if (!z) list(y) else (y), x, lists, SIMPLIFY=FALSE)
        x<-do.call('c', x)
        lists <- sapply(x, class)=="list"
    }
    x
}
library(rlist)
list.flatten

Upvotes: 5

MrFlick
MrFlick

Reputation: 206401

You could do something like this

delist<-function(x) {
    lists <- sapply(x, class)=="list"
    while(any(lists)) {
        x<-mapply(function(y,z) if (!z) list(y) else (y), x, lists, SIMPLIFY=FALSE)
        x<-do.call('c', x)
        lists <- sapply(x, class)=="list"
    }
    x
}

with your example you get

delist(B)
# [[1]]
#      [,1] [,2] [,3] [,4] [,5]
# [1,]    1    3    5    7    9
# [2,]    2    4    6    8   10
# 
# [[2]]
#      [,1] [,2] [,3] [,4] [,5]
# [1,]    1    3    5    7    9
# [2,]    2    4    6    8   10
# 
# [[3]]
#      [,1] [,2] [,3] [,4] [,5]
# [1,]    1    3    5    7    9
# [2,]    2    4    6    8   10

Upvotes: 12

Related Questions