Liste öğesinin olup olmadığı nasıl test edilir?


113

Sorun

Bir listenin bir öğesinin olup olmadığını test etmek istiyorum, işte bir örnek

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

Bu örnekte foo$avar olduğunu biliyorum , ancak test geri dönüyor FALSE.

İçeri baktım ?existsve with(foo, exists('a')geri döndüğünü buldum TRUE, ancak neden exists('foo$a')geri geldiğini anlamıyorum FALSE.

Sorular

  • Neden exists('foo$a')geri dönüyor FALSE?
  • Kullanımı mı with(...)tercih yaklaşımı?

1
belki !is.null(foo$a)(veya !is.null(foo[["a"]])güvenli tarafta olmak)? (veya exists("a",where=foo))
Ben Bolker

1
@BenBolker teşekkürler - iyi bir cevap olur; neden ikinci seçenek tercih ediliyor?
David LeBauer

3
@David kısmi eşleştirme ... yukarıdakileri deneyinfoo <- list(a1=1)
baptiste

Yanıtlar:


151

Bu aslında düşündüğünüzden biraz daha yanıltıcı. Bir liste aslında (biraz çaba ile) NULL öğeler içerebileceğinden, kontrol etmek yeterli olmayabiliris.null(foo$a) . Daha katı bir test, adın listede gerçekten tanımlandığını kontrol etmek olabilir:

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

... ve foo[["a"]]daha güvenlidir foo$açünkü ikincisi kısmi eşleme kullanır ve bu nedenle daha uzun bir adla da eşleşebilir:

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

[GÜNCELLEME] Öyleyse, neden exists('foo$a')çalışmıyor sorusuna geri dönelim . existsFonksiyon sadece kontrol değişken bir ortamda mevcut değilse de, bir nesne mevcut parçaları. Dize "foo$a"edebi olarak yorumlanır: "foo $ a" adında bir değişken var mı? ... ve cevap 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

2
hala net değil - neden var exists('foo$a') == FALSEmı?
David LeBauer

Bu, bunun için genellikle R! 'De iyi bir çözüm olmadığını gösterir! $mylist[[12]]$out$mcerrorŞu anda cehennem gibi karmaşık olan daha karmaşık şeyler ( tanımlanmışsa test etmek gibi ) isteyebilir.
TMS

@ Jim'in cevabında belirtilen whereargümanın farkında mıydınız ? exists
David LeBauer

"bar$a" <- 42Bunun geçersiz sözdizimi olmasını ve saf anlamda çalışmış olmasını ("foo $ a") gerçekten isterdim.
Andy V

44

Adlandırılmış öğeleri kontrol etmenin en iyi yolu kullanmaktır exist(), ancak yukarıdaki yanıtlar işlevi düzgün kullanmamaktadır. Sen kullanmak gerekir wheredeğişkenin kontrol etmek için argüman içinde listede.

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

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

8
exists()Bir liste üzerinde kullanmak işe yarıyor, ancak R'nin bu isimdeki bir nesneyi kontrol etmeden önce onu dahili olarak bir ortama zorladığına inanıyorum ki bu verimsizdir ve isimsiz elemanlar varsa hatalara neden olabilir. Çalıştırmak Örneğin exists('a', list(a=1, 2)), bu bir hata verecektir: Error in list2env(list(a = 1, 2), NULL, <environment>) : attempt to use zero-length variable name. Dönüşüm burada gerçekleşir: github.com/wch/r-source/blob/…
wch

5

Diğer cevaplarda önerilen yöntemlerin performans karşılaştırması.

> 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

Listeyi birçok kez erişilen hızlı bir sözlük olarak kullanmayı planlıyorsanız, bu is.nullyaklaşım tek geçerli seçenek olabilir. %in%Yaklaşım O (n) iken O (1) olduğunu varsayıyorum ?


4

@ Salient.salamander'in hafif değiştirilmiş bir versiyonu, eğer biri tam yolu kontrol etmek isterse, bu kullanılabilir.

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

Henüz ortaya çıkmamış bir çözüm, NULL'u başarıyla işleyen uzunluk kullanmaktır. Anlayabildiğim kadarıyla, NULL dışındaki tüm değerlerin uzunluğu 0'dan büyük.

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

Böylece hem adlandırılmış hem de numaralandırılmış endekslerle çalışan basit bir işlev yapabiliriz:

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

Öğe mevcut değilse, tryCatch bloğu tarafından yakalanan bir sınır dışı koşuluna neden olur.


3

rlang::has_name() bunu da yapabilir:

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

Gördüğünüz gibi, @Tommy'nin temel R'yi nasıl kullanacağını gösterdiği tüm durumları doğal olarak ele alıyor ve adsız öğeler içeren listeler için çalışıyor. exists("bb", where = foo)Okunabilirlik için başka bir cevapta önerildiği gibi yine de tavsiye ederim , ancak has_nameadsız öğeleriniz varsa bir alternatiftir.


0

Bir liste öğesinin değeriylepurrr::has_element karşılaştırmak için kullanın :

> 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

Öğe yuvalanmışsa / herhangi bir yuvalama düzeyindeyse çalışır mı? Belgeleri kontrol ettim ve net değildi
David LeBauer

@DavidLeBauer, hayır. Bu durumda, rapply(gibi any(rapply(x, function(v) identical(v, c(3, 4)), how = 'unlist'))) kullanırım
Dmitry Zotikov
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.