How to bind two lists with same structure?
Here's a go at it:
library(purrr)
rec_map <- function(fizz, buzz) {
if(is.atomic(fizz) | is.null(names(fizz))){
c(fizz, buzz)
} else {
imap(fizz,
~rec_map(fizz[[.y]], buzz[[.y]]))
}
}
temp <- rec_map(foo, bar)
all.equal(temp, wonderful)
#> [1] TRUE
I'm by no means a computer scientist, so take the solution with a grain of salt. I am not certain about the behavior desired when there are no names for one level, but then one level down there are names (e.g., foo$c
). So I just combined the results (c()
) if we encountered a level without names.
edit to take a number of lists:
prec_map <- function(...){
dots <- list(...)
first_el = dots[[1]]
if(is.atomic(first_el) | is.null(names(first_el))){
do.call(c, dots)
} else {
imap(first_el,
function(el, nme){
one_level_down <- map(dots, nme)
do.call(prec_map, one_level_down)
})
}
}
temp <- prec_map(foo, bar)
all.equal(temp, wonderful)
[1] TRUE
I haven't tested it out thoroughly, but light testing looks like it gets the job done.
list_merge
does something close to the requirements:
library(purrr)
res <- list_merge(foo, !!! bar)
all.equal(wonderful, list_merge(foo, !!! bar))
# [1] "Component “c”: Length mismatch: comparison on first 3 components"
# [2] "Component “c”: Component 1: Component 1: Numeric: lengths (3, 6) differ"
# [3] "Component “c”: Component 2: Component 1: Numeric: lengths (3, 6) differ"
The only difference seems to be for elements that are unnamed lists (e.g. foo$c
and bar$c
), the elements of which are concatenated by position (foo$c[[1]]
with bar$c[[1]]
, foo$c[[2]]
with bar$c[[2]]
, and foo$c[[3]]
left alone since there is no bar$c[[3]]
... rather than c(foo$c, bar$c)
).
And a parallel version could be:
plist_merge <- function(.l) {
reduce(.l, ~ list_merge(.x, !!! .y))
}
all.equal(
plist_merge(list(foo, bar)),
list_merge(foo, !!! bar)
)
# [1] TRUE