从列表的列表中移除NULL元素

37

如何在R中从嵌套列表中删除空元素,例如下面的列表:

lll <- list(list(NULL),list(1),list("a"))

我想要的物品应该长这样:

lll <- list(list(1),list("a"))

我在这里看到过类似的答案:如何从列表中删除元素?,但无法将其扩展为包含多个列表的列表。

编辑

上面是我举的一个不好的例子。这两个答案都适用于更简单的情况(上文)。如果列表像这样:

lll <- list(list(NULL),list(1,2,3),list("a","b","c"))

获取方法:

lll <- list(list(1,2,3),list("a","b","c"))

也许接受以下解决方案之一? - zx8754
7个回答

30

这个递归解决方案的优点是可以处理更深度嵌套的列表。

它的设计非常类似于Gabor Grothendieck对于这个相当类似的问题的回答。如果函数还需要删除像list(NULL)(不同于NULL)这样的对象,那么就需要我修改那段代码,以满足您的要求。

## A helper function that tests whether an object is either NULL _or_ 
## a list of NULLs
is.NullOb <- function(x) is.null(x) | all(sapply(x, is.null))

## Recursively step down into list, removing all such objects 
rmNullObs <- function(x) {
   x <- Filter(Negate(is.NullOb), x)
   lapply(x, function(x) if (is.list(x)) rmNullObs(x) else x)
}

rmNullObs(lll)
# [[1]]
# [[1]][[1]]
# [1] 1
# 
# 
# [[2]]
# [[2]][[1]]
# [1] "a"

这里是将其应用于更深层嵌套列表的示例,其他目前提出的解决方案在不同程度上都失败了。

LLLL <- list(lll)
rmNullObs(LLLL)
# [[1]]
# [[1]][[1]]
# [[1]][[1]][[1]]
# [[1]][[1]][[1]][[1]]
# [1] 1
# 
# 
# [[1]][[1]][[2]]
# [[1]][[1]][[2]][[1]]
# [1] "a"

这是一个很棒的函数! - mrp
1
@mrp -- 谢谢!很高兴我能把这个方便的模板传给别人。 - Josh O'Brien
1
顺便提一下,如果您的列表元素主要是数据帧,则此代码可能会表现出人们所不期望的方式,因为 is.list 将捕获并解开它们。我认为只需添加一个额外的 && is.data.frame(x) 就足够了。 - joran
@joran -- 谢谢。你的意思是 && !is.data.frame(x),对吗? - Josh O'Brien

21

这里有一个使用 FilterNegate 结合的选项

Filter(Negate(function(x) is.null(unlist(x))), lll)
# [[1]]
# [[1]][[1]]
# [1] 1
#
#
# [[2]]
# [[2]][[1]]
# [1] "a"

1
我正在使用S4对象列表,这个解决方案是唯一有效的,谢谢! - Rafael

9

使用 purrr

purrr::map(lll, ~ purrr::compact(.)) %>% purrr::keep(~length(.) != 0)
[[1]]
[[1]][[1]]
[1] 1

[[1]][[2]]
[1] 2

[[1]][[3]]
[1] 3


[[2]]
[[2]][[1]]
[1] "a"

[[2]][[2]]
[1] "b"

[[2]][[3]]
[1] "c"

7

对于这个特定的例子,您还可以使用unlist和它的recursive参数。

lll[!sapply(unlist(lll, recursive=FALSE), is.null)]
# [[1]]
# [[1]][[1]]
# [1] 1
#
#
# [[2]]
# [[2]][[1]]
# [1] "a"

6

由于您有嵌套的列表,因此您可能需要运行两次 l/sapply,例如:

lll[!sapply(lll,sapply,is.null)]

#[[1]]
#[[1]][[1]]
#[1] 1
#
#
#[[2]]
#[[2]][[1]]
#[1] "a"

5

CRAN上有一个新的包rlist,感谢任琨让我们的生活更轻松。

    list.clean(.data, fun = is.null, recursive = FALSE)

或者用于递归删除 NULL:

    list.clean(.data, fun = is.null, recursive = TRUE)

包的名称是什么?链接呢? - not2qubit
抱歉 @not2qubit,包的名称就像上面提到的一样是RLIST。 - Raminsu

0

对Josh O'Brien的解决方案进行快速修复。列表函数存在一些问题。

is.NullOb <- function(x) if(!(is.function(x))) is.null(x) | all(sapply(x, is.null)) else FALSE

## Recursively step down into list, removing all such objects 
rmNullObs <- function(x) {
  if(!(is.function(x))) {
    x = x[!(sapply(x, is.NullOb))]
    lapply(x, function(x) if (is.list(x)) rmNullObs(x) else x)
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接