Dollar operator as function argument for sapply not working as expected
Asked Answered
U

2

14

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?

Ummersen answered 31/12, 2015 at 10:21 Comment(0)
C
6

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.

Chain answered 31/12, 2015 at 12:47 Comment(2)
Very interesting answer! It basically shows that already x="a";"$"(test_list[[1]],x); gives an incorrect result.Ummersen
Also, similar to what you have said, "$"(test_list[[1]],a) gives 1, although no object a was defined.Ummersen
L
12

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.

Lawford answered 31/12, 2015 at 13:36 Comment(0)
C
6

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.

Chain answered 31/12, 2015 at 12:47 Comment(2)
Very interesting answer! It basically shows that already x="a";"$"(test_list[[1]],x); gives an incorrect result.Ummersen
Also, similar to what you have said, "$"(test_list[[1]],a) gives 1, although no object a was defined.Ummersen

© 2022 - 2024 — McMap. All rights reserved.