在列表的嵌套列表中查找空列表

Find empty lists in nested list of lists

给定一个任意嵌套的列表,我如何判断一个列表是否包含空列表?考虑以下示例:

mylist <- list(list("foo", "bar", "baz", list(list())))

我试过 rapply,但它跳过了列表。虽然我可以使用 lapply,但我需要事先知道嵌套级别。对于这个练习,我不需要知道列表在哪里(尽管那将是一个奖励),我只需要一种方法来检测是否有一个列表。

这样的函数怎么样

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)
    }
}

基本上我们创建了一个递归函数来查找长度为 0 的列表。

has_empty_list( list(list("foo", "bar", "baz", list(list()))) )
# TRUE
has_empty_list( list(list("foo", "bar", "baz", list(list(4)))) )
# FALSE

这里修改一下找到空列表的索引

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())
    }
}

这应该 return 一个索引向量,您可以使用它来查找空列表。例如

( i <- find_empty_list(mylist) )
# [1] 1 4 1
mylist[[i]]
# list()

如果第一个参数本身是一个空列表,它将return 0

find_empty_list(list())
# 0

如果没有空列表,它应该return一个空向量

find_empty_list(list(1:3, list("c", a~b)))
# numeric()

使用嵌套列表的另一个方便的选项是使用 data.tree 包:

library(data.tree)
nodes <- as.Node(mylist)
any(node$Get(function(node) length(as.list(node))) == 0)
# [1] TRUE

另一种方法是在 rrapply 包中使用 rrapply(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

将上述调用更新为 return 任何空列表的索引向量很简单:

## 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

在这里,我们使用 .xpos 参数计算当前列表元素的位置。


请注意,这会自动 return 所有空列表位置,而不是只有一个:

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