Recasting nested list of any depth

Viewed 54

Assume this simplified example:

L <- list()

L$Foo <- list()
L$Foo$Bar <- list()
L$Foo$Bar$VAR <- TRUE

L$Lorem <- list()
L$Lorem$Ipsum <- list()
L$Lorem$Ipsum$Dolor <- list()
L$Lorem$Ipsum$Dolor$VAR <- TRUE

I will then melt this list with reshape2::melt(L). That will output the following:

  value    L3    L2   L4    L1
1  TRUE   VAR   Bar <NA>   Foo
2  TRUE Dolor Ipsum  VAR Lorem

After some operations on certain cells in the value column, I'm then looking to recast this melted list into the exact same nested list structure as L—the only difference being that I updated a few of the value instances.

Any ideas how to achieve this? Please keep in mind that the nested lists can have any, and varying, depth.

2 Answers

An option is relist, after we unlisted L

tmp <- unlist(L)
# make small changes
tmp[] <- FALSE
relist(tmp, L)

Result

$Foo
$Foo$Bar
$Foo$Bar$VAR
[1] FALSE



$Lorem
$Lorem$Ipsum
$Lorem$Ipsum$Dolor
$Lorem$Ipsum$Dolor$VAR
[1] FALSE

L looks like

$Foo
$Foo$Bar
$Foo$Bar$VAR
[1] TRUE



$Lorem
$Lorem$Ipsum
$Lorem$Ipsum$Dolor
$Lorem$Ipsum$Dolor$VAR
[1] TRUE

An alternative could be to use rrapply() in the rrapply-package which has options how = "melt" and how = "unmelt" to transform between nested lists and melted data.frames:

library(rrapply)

L <- list(Foo = list(Bar = list(VAR = TRUE)), Lorem = list(Ipsum = list(Dolor = list(VAR = TRUE))))

## melt to data.frame
(L1 <- rrapply(L, how = "melt"))
#>      L1    L2    L3   L4 value
#> 1   Foo   Bar   VAR <NA>  TRUE
#> 2 Lorem Ipsum Dolor  VAR  TRUE

## cast back to nested list
L2 <- rrapply(L1, how = "unmelt")

str(L2)
#> List of 2
#>  $ Foo  :List of 1
#>   ..$ Bar:List of 1
#>   .. ..$ VAR: logi TRUE
#>  $ Lorem:List of 1
#>   ..$ Ipsum:List of 1
#>   .. ..$ Dolor:List of 1
#>   .. .. ..$ VAR: logi TRUE

identical(L2, L)
#> [1] TRUE

An important advantage with respect to relist() is that no list skeleton object is needed (see ?relist), so we are not constrained by the list format defined in the skeleton object when modifying the melted data.frame, e.g.:

L_unlist <- unlist(as.relistable(L))

## this change has no effect when relisting
## as the original list is used as skeleton
names(L_unlist)[1] <- "Foo.Bar.Test"

relist(L_unlist)
#> $Foo
#> $Foo$Bar
#> $Foo$Bar$VAR
#> [1] TRUE
#> 
#> $Lorem
#> $Lorem$Ipsum
#> $Lorem$Ipsum$Dolor
#> $Lorem$Ipsum$Dolor$VAR
#> [1] TRUE
#> 
#> attr(,"class")
#> [1] "relistable" "list"

## here it does behave as expected
L_melt <- rrapply(L, how = "melt")
L_melt[1, "L3"] <- "Test"

rrapply(L_melt, how = "unmelt")
#> $Foo
#> $Foo$Bar
#> $Foo$Bar$Test
#> [1] TRUE
#> 
#> $Lorem
#> $Lorem$Ipsum
#> $Lorem$Ipsum$Dolor
#> $Lorem$Ipsum$Dolor$VAR
#> [1] TRUE
Related