我想测试一个列表的元素是否存在,这是一个例子
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(...)
首选方法吗?答案 0 :(得分:127)
这实际上比你想象的要复杂一些。由于列表实际上(通过一些努力)可以包含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
答案 1 :(得分:35)
检查命名元素的最佳方法是使用exist()
,但上述答案未正确使用该功能。您需要使用where
参数来检查列表中的变量。
foo <- list(a=42, b=NULL)
exists('a', where=foo) #TRUE
exists('b', where=foo) #TRUE
exists('c', where=foo) #FALSE
答案 2 :(得分:4)
@ 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)
})
}
答案 3 :(得分:3)
尚未出现的一个解决方案是使用length,它成功处理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块捕获的越界条件。
答案 4 :(得分:3)
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显示的如何使用基数R的所有情况,并适用于带有未命名项的列表。我仍然建议使用exists("bb", where = foo)
,这是另一个答案,以提高可读性,但是如果您有未命名的物品,可以使用has_name
。
答案 5 :(得分:2)
以下是其他答案中提出的方法的性能比较。
> 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)?
答案 6 :(得分:1)
使用purrr::has_element
来检查列表元素的 value :
> 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