Consider a case like this:
xml_list <- list(
a = "7",
b = list("8"),
c = list(
c.a = "7",
c.b = list("8"),
c.c = list("9", "10"),
c.d = c("11", "12", "13")),
d = c("a", "b", "c"))
what I'm looking for is a way of how to simplify this construct recursively such that unlist
is called on any list
of length 1. The expected result for above example would look like:
list(
a = "7",
b = "8",
c = list(
c.a = "7",
c.b = "8",
c.c = list("9", "10"),
c.d = c("11", "12", "13")),
d = c("a", "b", "c"))
I have dabbled with rapply
, but that explicitly operates on list
-members that are NOT lists themselves, so wrote the following:
library(magrittr)
clean_up_list <- function(xml_list){
xml_list %>%
lapply(
function(x){
if(is.list(x)){
if(length(x) == 1){
x %<>%
unlist()
} else {
x %<>%
clean_up_list()
}
}
return(x)
})
}
This, however, I can't even test, as Error: C stack usage 7969588 is too close to the limit
(at least on lists that I terminally want to process).
Digging deeper (and after mulling over @Roland's response), I came up with a solution that utilizes purrr
-goodness, reversely iterates over list depth and NEARLY does what I want:
clean_up_list <- function(xml_list)
{
list_depth <- xml_list %>%
purrr::vec_depth()
for(dl in rev(sequence(list_depth)))
{
xml_list %<>%
purrr::modify_depth(
.depth = dl,
.ragged = TRUE,
.f = function(x)
{
if(is.list(x) && length(x) == 1 && length(x[[1]]) == 1)
{
unlist(x, use.names = FALSE)
} else {
x
}
})
}
return(xml_list)
}
This appears to work as intended even for lists of the depth I'm dealing with BUT elements that used to be vectors (like c.d
and d
in the example) now are converted to lists
, which defeats the purpose ... any further insight?