Find empty lists in nested list of lists
Another convenient option to work with nested list is to use data.tree
package:
library(data.tree)
nodes <- as.Node(mylist)
any(node$Get(function(node) length(as.list(node))) == 0)
# [1] TRUE
What about a function like this
has_empty_list <- function(x) {
if(is.list(x)) {
if (length(x)==0) {
return(TRUE)
} else {
return(any(vapply(x, has_empty_list, logical(1))))
}
} else {
return(FALSE)
}
}
Basically we create a recursive function to look for lists of length 0.
has_empty_list( list(list("foo", "bar", "baz", list(list()))) )
# TRUE
has_empty_list( list(list("foo", "bar", "baz", list(list(4)))) )
# FALSE
And here's a modification to find the index of the empty list
find_empty_list <- function(x, index=c()) {
if(is.list(x)) {
#list
if (length(x)==0) {
if (length(index)==0) {
return(0)
} else {
return(index)
}
} else {
m <- Map(find_empty_list, x, lapply(seq_along(x), function(i) append(index,i)))
# return the most deeply nested
return( m[[which.max(lengths(m))]] )
}
} else {
return(numeric())
}
}
This should return a vector of the index that you can use to find the empty list. For example
( i <- find_empty_list(mylist) )
# [1] 1 4 1
mylist[[i]]
# list()
If the first parameter itself is an empty list, it will return 0
find_empty_list(list())
# 0
and if there is no empty list, it should return an empty vector
find_empty_list(list(1:3, list("c", a~b)))
# numeric()
Another approach is to use rrapply
in the rrapply
-package (an extension of base-rrapply
):
library(rrapply)
## check if any empty list exists
any(
rrapply(mylist,
classes = "list",
condition = function(x) length(x) < 1,
f = function(x) TRUE,
deflt = FALSE,
how = "unlist"
)
)
#> [1] TRUE
It is straightforward to update the above call to return the index vectors of any empty lists:
## return flat list with position vectors of empty list
rrapply(mylist,
classes = "list",
condition = function(x) length(x) < 1,
f = function(x, .xpos) .xpos,
how = "flatten"
)
#> [[1]]
#> [1] 1 4 1
Here, we make use of the .xpos
argument which evaluates to the position of the current list element under evaluation.
Note that this automatically returns all empty list positions instead of only one:
mylist2 <- list(list("foo", list(), "baz", list(list())))
rrapply(mylist2,
classes = "list",
condition = function(x) length(x) < 1,
f = function(x, .xpos) .xpos,
how = "flatten"
)
#> [[1]]
#> [1] 1 2
#>
#> [[2]]
#> [1] 1 4 1
## using MrFlick's find_empty_list function
find_empty_list(mylist2)
#> [1] 1 4 1