Reputation: 16147
I reduced some problem to the following toy code:
cc<-c("1","2")
ff<-function(x) { list(myname=x)}
aa<-unlist(lapply(cc,ff))
bb<-sapply(cc,ff)
I'd expect aa and bb to be identical, but:
> aa
myname myname
"1" "2"
> bb
$`1.myname`
[1] "1"
$`2.myname`
[1] "2"
I'm aware of the USE.NAMES argument to sapply, however -
USE.NAMES logical; if TRUE and if X is character, use X as names for the result unless it had names already.
and so should have no impact in this case,
What's going on here? Could this be an R issue?
Edit: after further investigation it turns out the root cause for the difference is that sapply is essentially equivalent not to
unlist(lapply(cc,ff)
but rather to
unlist(lapply(cc, ff), recursive = FALSE)
(This is the exact internal unlist call).
Upvotes: 2
Views: 169
Reputation: 173888
Look carefully at this:
lapply(cc, ff)
#> [[1]]
#> [[1]]$myname
#> [1] "1"
#>
#>
#> [[2]]
#> [[2]]$myname
#> [1] "2"
The output of lapply
itself doesn't have names. Look:
a <- lapply(cc, ff)
names(a)
#> NULL
The output of the lapply
is actually an unnamed list. Each element of a
is a named list.
names(a[[1]])
#> [1] "myname"
names(a[[2]])
#> [1] "myname"
So in fact, USE.NAMES
will apply, and sapply
will assign the contents of cc
as names for the output of the lapply
for which sapply
is a thin wrapper as stated in the documentation. It's quite straightforward to follow the code through:
sapply
#> function (X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)
#> {
#> FUN <- match.fun(FUN)
#> answer <- lapply(X = X, FUN = FUN, ...)
#> if (USE.NAMES && is.character(X) && is.null(names(answer)))
#> names(answer) <- X
#> if (!isFALSE(simplify) && length(answer))
#> simplify2array(answer, higher = (simplify == "array"))
#> else answer
#> }
#> <bytecode: 0x036ae7a8>
#> <environment: namespace:base>
Upvotes: 1