Problem
Chciałbym sprawdzić, czy istnieje element listy, oto przykład
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
W tym przykładzie wiem, że foo$a
istnieje, ale test powraca FALSE
.
Zajrzałem ?exists
i znalazłem, że with(foo, exists('a')
powraca TRUE
, ale nie rozumiem, dlaczego exists('foo$a')
wraca FALSE
.
pytania
- Dlaczego
exists('foo$a')
wracaFALSE
? - Czy jest stosowane
with(...)
preferowane podejście?
!is.null(foo$a)
(lub!is.null(foo[["a"]])
po bezpiecznej stronie)? (lubexists("a",where=foo)
)foo <- list(a1=1)
Odpowiedzi:
W rzeczywistości jest to trochę trudniejsze niż myślisz. Ponieważ lista może faktycznie (przy pewnym wysiłku) zawierać elementy NULL, sprawdzenie może nie wystarczyć
is.null(foo$a)
. Bardziej rygorystycznym testem może być sprawdzenie, czy nazwa jest rzeczywiście zdefiniowana na liście:... i
foo[["a"]]
jest bezpieczniejszy niżfoo$a
, ponieważ ten ostatni używa dopasowania częściowego, a zatem może również dopasować dłuższą nazwę:[AKTUALIZACJA] Wracając do pytania, dlaczego
exists('foo$a')
nie działa. Plikexists
Jedyna funkcja sprawdza czy zmienna istnieje w środowisku, jeżeli nie częściami exist obiektu. Łańcuch"foo$a"
jest interpretowany literacko: czy istnieje zmienna o nazwie „foo $ a”? ... a odpowiedź brzmiFALSE
...źródło
exists('foo$a') == FALSE
?$mylist[[12]]$out$mcerror
jest zdefiniowane), które obecnie byłyby bardzo skomplikowane.where
argumentexists
wskazano w @ Jima odpowiedź ?"bar$a" <- 42
Naprawdę chciałbym, żeby to była nieprawidłowa składnia i istnienie ("foo $ a") działało w naiwnym sensie.Najlepszym sposobem sprawdzenia nazwanych elementów jest użycie
exist()
, jednak powyższe odpowiedzi nie używają tej funkcji poprawnie. Trzeba użyćwhere
argumentu aby sprawdzić zmiennej wewnątrz listy.źródło
exists()
na liście działa, ale uważam, że R wewnętrznie wymusza użycie go do środowiska przed sprawdzeniem obiektu o tej nazwie, co jest nieefektywne i może powodować błędy, jeśli są jakieś nienazwane elementy. Na przykład, jeśli uruchomićexists('a', list(a=1, 2))
, to daje błąd:Error in list2env(list(a = 1, 2), NULL, <environment>) : attempt to use zero-length variable name
. Konwersja odbywa się tutaj: github.com/wch/r-source/blob/ ...Oto porównanie wydajności proponowanych metod w innych odpowiedziach.
Jeśli planujesz używać listy jako szybkiego słownika, do którego uzyskuje się wiele razy, to
is.null
podejście może być jedyną realną opcją. Zakładam, że to jest O (1), podczas gdy%in%
podejście to O (n)?źródło
Lekko zmodyfikowana wersja @ salient.salamander, jeśli ktoś chce sprawdzić pełną ścieżkę, może być użyty.
źródło
Jednym z rozwiązań, które jeszcze się nie pojawiło, jest użycie długości, która z powodzeniem obsługuje NULL. O ile wiem, wszystkie wartości z wyjątkiem NULL mają długość większą niż 0.
W ten sposób moglibyśmy stworzyć prostą funkcję, która działa zarówno z nazwanymi, jak i numerowanymi indeksami:
Jeśli element nie istnieje, powoduje to stan poza zakresem przechwycony przez blok tryCatch.
źródło
rlang::has_name()
też może to zrobić:Jak widać, z natury obsługuje on wszystkie przypadki, które @Tommy pokazał, jak obsługiwać przy użyciu podstawowego R i działa dla list z nienazwanymi elementami. Nadal polecałbym
exists("bb", where = foo)
zgodnie z propozycją w innej odpowiedzi na czytelność, alehas_name
jest to alternatywa, jeśli masz nienazwane elementy.źródło
Służy
purrr::has_element
do sprawdzania wartości elementu listy:źródło
rapply
any(rapply(x, function(v) identical(v, c(3, 4)), how = 'unlist'))