如何测试列表元素是不是存在?

Posted

技术标签:

【中文标题】如何测试列表元素是不是存在?【英文标题】:How to test if list element exists?如何测试列表元素是否存在? 【发布时间】:2011-12-04 21:44:47 【问题描述】:

问题

我想测试一个列表的元素是否存在,这里是一个例子

foo <- list(a=1)
exists('foo') 
TRUE   #foo does exist
exists('foo$a') 
FALSE  #suggests that foo$a does not exist
foo$a
[1] 1  #but it does exist

在这个例子中,我知道foo$a 存在,但测试返回FALSE

查看?exists,发现with(foo, exists('a')返回TRUE,但不明白为什么exists('foo$a')返回FALSE

问题

为什么exists('foo$a') 返回FALSE? 使用with(...) 是首选方法吗?

【问题讨论】:

也许是!is.null(foo$a)(或!is.null(foo[["a"]])为了安全起见)? (或exists("a",where=foo) @BenBolker 谢谢 - 会是一个很好的答案;为什么首选后者? @David 部分匹配...试试上面的foo &lt;- list(a1=1) 【参考方案1】:

这实际上比您想象的要复杂一些。由于列表实际上可以(通过一些努力)包含 NULL 元素,因此检查 is.null(foo$a) 可能还不够。更严格的测试可能是检查名称是否确实在列表中定义:

foo <- list(a=42, b=NULL)
foo

is.null(foo[["a"]]) # FALSE
is.null(foo[["b"]]) # TRUE, but the element "exists"...
is.null(foo[["c"]]) # TRUE

"a" %in% names(foo) # TRUE
"b" %in% names(foo) # TRUE
"c" %in% names(foo) # FALSE

...而且foo[["a"]]foo$a 更安全,因为后者使用部分匹配,因此也可能匹配更长的名称:

x <- list(abc=4)
x$a  # 4, since it partially matches abc
x[["a"]] # NULL, no match

[更新] 所以,回到为什么exists('foo$a') 不起作用的问题。 exists 函数仅检查环境中是否存在变量,而不检查对象的某些部分是否存在。字符串"foo$a" 被解释为文学:是否有一个名为“foo$a”的变量? ...答案是FALSE...

foo <- list(a=42, b=NULL) # variable "foo" with element "a"
"bar$a" <- 42   # A variable actually called "bar$a"...
ls() # will include "foo" and "bar$a" 
exists("foo$a") # FALSE 
exists("bar$a") # TRUE

【讨论】:

目前还不清楚 - exists('foo$a') == FALSE 有什么原因吗? 这表明在 R 中通常没有好的解决方案!人们可能想要更复杂的事情(比如测试是否定义了$mylist[[12]]$out$mcerror),而这目前会非常复杂。 您知道@Jim 的answer 中指出的existswhere 参数吗? "bar$a" &lt;- 42 我真的希望这是无效的语法,exists("foo$a") 在幼稚的意义上工作。【参考方案2】:

检查命名元素的最佳方法是使用exist(),但是上面的答案没有正确使用该函数。您需要使用where 参数来检查列表中的变量

foo <- list(a=42, b=NULL)

exists('a', where=foo) #TRUE
exists('b', where=foo) #TRUE
exists('c', where=foo) #FALSE

【讨论】:

在列表上使用exists() 确实有效,但我相信 R 在检查该名称的对象之前会在内部将其强制转换为环境,这是低效的,如果有任何可能会导致错误未命名的元素。例如如果你运行exists('a', list(a=1, 2)),它会报错:Error in list2env(list(a = 1, 2), NULL, &lt;environment&gt;) : attempt to use zero-length variable name。转换发生在这里:github.com/wch/r-source/blob/…【参考方案3】:

这是其他答案中提出的方法的性能比较。

> foo <- sapply(letters, function(x)runif(5), simplify = FALSE)
> microbenchmark::microbenchmark('k' %in% names(foo), 
                                 is.null(foo[['k']]), 
                                 exists('k', where = foo))
Unit: nanoseconds
                     expr  min   lq    mean median   uq   max neval cld
      "k" %in% names(foo)  467  933 1064.31    934  934 10730   100  a 
      is.null(foo[["k"]])    0    0  168.50      1  467  3266   100  a 
 exists("k", where = foo) 6532 6998 7940.78   7232 7465 56917   100   b

如果您打算将列表用作多次访问的快速字典,那么is.null 方法可能是唯一可行的选择。我假设它是 O(1),而 %in% 方法是 O(n)?

【讨论】:

【参考方案4】:

一个尚未提出的解决方案是使用长度,它可以成功处理 NULL。据我所知,除 NULL 之外的所有值的长度都大于 0。

x <- list(4, -1, NULL, NA, Inf, -Inf, NaN, T, x = 0, y = "", z = c(1,2,3))
lapply(x, function(el) print(length(el)))
[1] 1
[1] 1
[1] 0
[1] 1
[1] 1
[1] 1
[1] 1
[1] 1
[1] 1
[1] 1
[1] 3

因此,我们可以创建一个简单的函数,可以同时使用命名索引和编号索引:

element.exists <- function(var, element)

  tryCatch(
    if(length(var[[element]]) > -1)
      return(T)
  , error = function(e) 
    return(F)
  )

如果该元素不存在,则会导致 tryCatch 块捕获的越界条件。

【讨论】:

【参考方案5】:

@salient.salamander 的轻微修改版本,如果要检查完整路径,可以使用它。

Element_Exists_Check = function( full_index_path )
  tryCatch(
    len_element = length(full_index_path)
    exists_indicator = ifelse(len_element > 0, T, F)
      return(exists_indicator)
  , error = function(e) 
    return(F)
  )

【讨论】:

【参考方案6】:

使用purrr::has_element 检查列表元素的

> x <- list(c(1, 2), c(3, 4))
> purrr::has_element(x, c(3, 4))
[1] TRUE
> purrr::has_element(x, c(3, 5))
[1] FALSE

【讨论】:

如果元素嵌套/在任何嵌套级别,它是否有效?我检查了文档,但不清楚 @DavidLeBauer,没有。在这种情况下,我会使用rapply(类似于any(rapply(x, function(v) identical(v, c(3, 4)), how = 'unlist'))【参考方案7】:

rlang::has_name() 也可以这样做:

foo = list(a = 1, bb = NULL)
rlang::has_name(foo, "a")  # TRUE
rlang::has_name(foo, "b")  # FALSE. No partial matching
rlang::has_name(foo, "bb")  # TRUE. Handles NULL correctly
rlang::has_name(foo, "c")  # FALSE

如您所见,它固有地处理@Tommy 展示的如何使用base R 处理的所有情况,并适用于具有未命名项目的列表。我仍然会推荐 exists("bb", where = foo) 以提高可读性,但如果您有未命名的项目,has_name 是另一种选择。

【讨论】:

以上是关于如何测试列表元素是不是存在?的主要内容,如果未能解决你的问题,请参考以下文章

在 Presto 中,如何检查我通过子查询获取的列表中是不是存在数组中的元素

F#:递归函数:测试元素是不是是给定列表的成员

测试 python 列表的所有元素是不是为 False

检查一个列表元素中的元素是不是存在于另一个列表中

如何检查Mysql中是不是存在数组元素?

如何检查 Firebase Firestore 的数组中是不是存在元素?