Given a nested list, how to create all possible lists from its elements, while preserving the structure of the nested list?
Nested list:
l = list(
a = list(
b = 1:2
),
c = list(
d = list(
e = 3:4,
f = 5:6
)
),
g = 7
)
Desired output: all possible combinations of the elements of l
, while preserving the structure, e.g.:
# One possible output:
list(
a = list(
b = 1
),
c = list(
d = list(
e = 3,
f = 5
)
),
g = 7
)
# Another possible output:
list(
a = list(
b = 1
),
c = list(
d = list(
e = 4,
f = 5
)
),
g = 7
)
My approach so far is to:
expand.grid()
and get a matrix where each row represents a unique combinationnames()
using regular expressionsI am looking for a less cumbersome approach because I have no guarantee that the names of the list elements will not change.
Add items to a Nested list. To add new values to the end of the nested list, use append() method. When you want to insert an item at a specific position in a nested list, use insert() method. You can merge one list into another by using extend() method.
A nested list is simply a list that occurs as an element of another list (which may of course itself be an element of another list, etc.). Common reasons nested lists arise are: They're matrices (a list of rows, where each row is itself a list, or a list of columns where each column is itself a list).
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With