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

Tags:

R

Nested Lists