从列表中删除NULL元素

121
mylist <- list(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 
    123, NULL, 456)

> mylist
[[1]]
NULL

[[2]]
NULL

[[3]]
NULL

[[4]]
NULL

[[5]]
NULL

[[6]]
NULL

[[7]]
NULL

[[8]]
NULL

[[9]]
NULL

[[10]]
NULL

[[11]]
[1] 123

[[12]]
NULL

[[13]]
[1] 456

我的列表有13个元素,其中11个是NULL。我想把它们删除,但保留非空元素的索引。

mylist2 = mylist[-which(sapply(mylist, is.null))]
> mylist2
[[1]]
[1] 123

[[2]]
[1] 456

这样可以很好地删除NULL元素,但我不希望非空元素被重新索引,即我希望mylist2看起来像这样,其中非空条目的索引被保留。

> mylist2
[[11]]
[1] 123

[[13]]
[1] 456

1
有人可能会找到一种方法,但我认为你正在陷入“为什么会打印出那样”的陷阱。这些索引数字不是您列表元素的名称。没有名称。检查names(mylist)。所以它们只是帮助显示元素在列表中的位置。这就是为什么您很难告诉R返回仅具有两个元素的列表的第11个位置。您可以尝试将列表命名为下面的答案。 - Pierre L
6
我认为这个答案应该更新为@Hayward-Oblad在下面提供的purrr解决方案。可以使用list %>% discard(is.null)或者list %>% discard(~ length(.x) == 0) - geotheory
8个回答

134

您能做到的最接近的方法是首先为列表元素命名,然后移除 NULL 值。

names(x) <- seq_along(x)

## Using some higher-order convenience functions
Filter(Negate(is.null), x)
# $`11`
# [1] 123
# 
# $`13`
# [1] 456

# Or, using a slightly more standard R idiom
x[sapply(x, is.null)] <- NULL
x
# $`11`
# [1] 123
# 
# $`13`
# [1] 456

22
将代码翻译成中文:x <- x[!sapply(x, is.null)]意思:将向量 x 中非空的元素赋值给向量 x。 - xm1
2
@xm1的答案更好,因为右侧表达式返回不带NULL的列表,因此可以编写一个方便的函数,如下所示:remove_null_lst <- function(x) x[!sapply(x, is.null)] - Prashant Bharadwaj

69

只需使用mylist[lengths(mylist) != 0]

lengths()函数是在R 3.2.0(2015年4月)引入的。


1
用户困在使用更旧版本的 R 时,可以使用 vapply(mylist, length, 1L),它略微更广泛(对于未受过训练的人来说可能更难阅读)。 - MichaelChirico

59

Tidyverse包中包含的purrr包具有优雅且快速的函数,可用于处理列表:

require(tidyverse)

# this works
compact(mylist)

# or this
mylist %>% discard(is.null)

# or this
# pipe "my_list" data object into function "keep()", make lambda function inside "keep()" to return TRUE FALSE.
mylist %>% keep( ~ !is.null(.) )


所有上述选项都来自Purrr。输出为:
[[1]] 
[1] 123

[[2]] 
[1] 456

注意:compact()曾经在plyr中,但dplyr取代了plyr,compact()仍然存在,但已移至purrr。无论如何,所有函数都在父包tidyverse中。
这是Purrr速查表下载链接:

https://rstudio.com/resources/cheatsheets/

或者直接在浏览器中查看Purrr备忘单:

https://evoldyn.gitlab.io/evomics-2018/ref-sheets/R_purrr.pdf


30

有一个函数可以自动删除列表中的所有空条目,并且如果该列表已命名,则保留非空条目的名称。

这个函数叫做compact,来自plyr包。

l <- list( NULL, NULL, foo, bar)
names(l) <- c( "one", "two", "three", "four" )

plyr::compact(l)
如果您想保留非空条目的索引,可以像之前的帖子中那样将列表命名,然后压缩列表:
names(l) <- seq_along(l)
plyr::compact(l)

12
补充一下:purrr::compact() 似乎可以完成相同的任务。 - Gabi

6
如果您想保留名称,可以执行以下操作:
a <- list(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 
          123, NULL, 456)
non_null_names <- which(!sapply(a, is.null))
a <- a[non_null_names]
names(a) <- non_null_names
a

您可以这样访问元素:
a[['11']]
num <- 11
a[[as.character(num)]]
a[[as.character(11)]]
a$`11`

虽然它们代表数字索引,但您无法使用整洁的[[11]][[13]]符号获取它们。


这是最接近你想要的,实际上可以做到的 :) - Felipe Gerard

6

这个解决方案同样适用于嵌套列表

rlist::list.clean(myNestedlist ,recursive = T)

太好了!我正在寻找这个。 - Nip

5

使用便利的链式表示法如下

library(magrittr)

mylist %>%
  setNames(seq_along(.)) %>%
  Filter(. %>% is.null %>% `!`, .)

7
Filter(Negate(is.null), setNames(L,seq_along(L))) 非常易读。 - thelatemail
好的,问题已经解决。否定操作符“!=”可以使用!替代。 - bramtayl

4

下面是只使用基础R函数非常简单的实现方法:

names(mylist) <- 1:length(mylist)
mylist2 <- mylist[which(!sapply(mylist, is.null))]

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