How to create all combinations from a nested list while preserving the structure using R?

The relist function from utils seems to be designed for this task:

rl <- as.relistable(l)
r <- expand.grid(data.frame(rl), KEEP.OUT.ATTRS = F)
> head(r, 5)
   b c.d.e c.d.f g
1  1     3     5 7
2  2     3     5 7
3  1     4     5 7
4  2     4     5 7
5  1     3     6 7

It saves the structure of the list (skeleton). This means one can now manipulate the data within the nested list and re-assign it into the structure (flesh). Here with the first row of the expanded matrix.

r <- rep(unname(unlist(r[1,])),each = 2)
l2 <- relist(r, skeleton = rl)
> l2
$a
$a$b
[1] 1 1


$c
$c$d
$c$d$e
[1] 3 3

$c$d$f
[1] 5 5



$g
[1] 7

attr(,"class")
[1] "relistable" "list"  

Note that since the structure stays the same, I need to supply the same amount of elements as in the original list. This is why used rep to repeat the element twice. One could also fill it with NA, I guess.

For every possible combination iterate through r (expanded):

lapply(1:nrow(r), function(x) 
          relist(rep(unname(unlist(r[x,])),each = 2), skeleton = rl))

Combining Ben Nutzer's brilliant answer and Joris Chau's brilliant comment, the answer will become a one-liner:

apply(expand.grid(data.frame(l)), 1L, relist, skeleton = rapply(l, head, n = 1L, how = "list")) 

It creates a list of lists with as many elements as rows returned by expand.grid(). The result is better visualised by the output of str():

str(apply(expand.grid(data.frame(l)), 1L, relist, skeleton = rapply(l, head, n = 1L, how = "list")))
List of 16
 $ :List of 3
  ..$ a:List of 1
  .. ..$ b: num 1
  ..$ c:List of 1
  .. ..$ d:List of 2
  .. .. ..$ e: num 3
  .. .. ..$ f: num 5
  ..$ g: num 7
 $ :List of 3
  ..$ a:List of 1
  .. ..$ b: num 2
  ..$ c:List of 1
  .. ..$ d:List of 2
  .. .. ..$ e: num 3
  .. .. ..$ f: num 5
  ..$ g: num 7
...
...
...
 $ :List of 3
  ..$ a:List of 1
  .. ..$ b: num 2
  ..$ c:List of 1
  .. ..$ d:List of 2
  .. .. ..$ e: num 4
  .. .. ..$ f: num 6
  ..$ g: num 7

Unequal sublist lengths

Here is an approach --extending on Uwe and Ben's answers-- that also works for arbitrary sublist lengths. Instead of calling expand.grid on data.frame(l), first flatten l to a single-level list and then call expand.grid on it:

## skeleton
skel <- rapply(l, head, n = 1L, how = "list")

## flatten to single level list
l.flat <- vector("list", length = length(unlist(skel)))
i <- 0L

invisible(
    rapply(l, function(x) {
          i <<- i + 1L
          l.flat[[i]] <<- x
        })
)

## expand all list combinations 
l.expand <- apply(expand.grid(l.flat), 1L, relist, skeleton = skel)

str(l.expand)
#> List of 12
#>  $ :List of 3
#>   ..$ a:List of 1
#>   .. ..$ b: num 1
#>   ..$ c:List of 1
#>   .. ..$ d:List of 2
#>   .. .. ..$ e: num 3
#>   .. .. ..$ f: num 5
#>   ..$ g: num 7
#>  ...
#>  ...
#>  $ :List of 3
#>   ..$ a:List of 1
#>   .. ..$ b: num 2
#>   ..$ c:List of 1
#>   .. ..$ d:List of 2
#>   .. .. ..$ e: num 4
#>   .. .. ..$ f: num 7
#>   ..$ g: num 7

Data

I slightly modified the data structure, so that the sublist components e and f are of unequal length.

l <- list(
    a = list(
        b = 1:2
    ),
    c = list(
        d = list(
            e = 3:4,
            f = 5:7
        )
    ),
    g = 7
)

## calling data.frame on l does not work
data.frame(l)
#> Error in (function (..., row.names = NULL, check.rows = FALSE, check.names = TRUE, : arguments imply differing number of rows: 2, 3

Tags:

List

Recursion

R