I have the following list
test_list=list(list(a=1,b=2),list(a=3,b=4))
and I want to extract all elements with list element name a
.
I can do this via
sapply(test_list,`[[`,"a")
which gives me the correct result
#[1] 1 3
When I try the same with Rs dollar operator $
, I get NULL
sapply(test_list,`$`,"a")
#[[1]]
#NULL
#
#[[2]]
#NULL
However, if I use it on a single element of test_list
it works as expected
`$`(test_list[[1]],"a")
#[1] 1
Am I missing something obvious here?
From what I've been able to determine it's a combination of two things.
First, the second element of $
is matched but not evaluated so it cannot be a variable.
Secondly, when arguments are passed to functions they are assigned to the corresponding variables in the function call. When passed to sapply
"a"
is assigned to a variable and therefore will no longer work with $
. We can see this by occurring by running
sapply("a", print)
[1] "a"
a
"a"
This can lead to peculiar results like this
sapply(test_list, function(x, a) {`$`(x, a)})
[1] 1 3
Where despite a
being a variable (which hasn't even been assigned) $
matches it to the names of the elements in the list.
evaluation vs. none
[[
evaluates its argument whereas $
does not. L[[a]]
gets the component of L
whose name is held in the variable a
. $
just passes the argument name itself as a character string so L$a
finds the "a"
component of L
. a
is not regarded as a variable holding the component name -- just a character string.
Below L[[b]]
returns the component of L
named "a"
because the variable b
has the value "a"
whereas L$b
returns the componet of L
named "b"
because with that syntax b
is not regarded as a variable but is regarded as a character string which itself is passed.
L <- list(a = 1, b = 2)
b <- "a"
L[[b]] # same as L[["a"]] since b holds a
## [1] 1
L$b # same as L[["b"]] since b is regarded as a character string to be passed
## [1] 2
sapply
Now that we understand the key difference bewteen $ and [[ to see what is going on with sapply
consider this example. We have made each element of test_list
into a "foo"
object and defined our own $.foo
and [[.foo
methods which simply show what R is passing to the method via the name
argument:
foo_list <- test_list
class(foo_list[[1]]) <- class(foo_list[[2]]) <- "foo"
"$.foo" <- "[[.foo" <- function(x, name) print(name)
result <- sapply(foo_list, "$", "a")
## "..."
## "..."
result2 <- sapply(foo_list, "[[", "a")
## [1] "a"
## [1] "a"
What is happening in the first case is that sapply
is calling whatever$...
and ...
is not evaluated so it would be looking for a list component which is literally named "..."
and, of course, there is no such component so whatever$...
is NULL hence the NULLs shown in the output in the question. In the second case whatever[[[...]]
evaluates to whatever[["a"]]
hence the observed result.
来源:https://stackoverflow.com/questions/34544888/dollar-operator-as-function-argument-for-sapply-not-working-as-expected