Ofek Shilon
Ofek Shilon

Reputation: 16147

R: sapply / lapply Different Behaviour with Names

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 -

  1. it's documented as -

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,

  1. Internally, it isn't even passed to simplify2array and thus neither to the final unlist.

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

Answers (1)

Allan Cameron
Allan Cameron

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

Related Questions