Skip to content

Peculiar interaction between sapply() and a (named) vctr #1416

Open
@jennybc

Description

@jennybc

I recently re-implemented an existing S3 class using vctrs and have noticed that sapply() / lapply() don't necessarily transfer the names of the input to the output. (I was not actually relying on this in any meaningful way, mind you, but I noticed that a piece of documentation started to render differently.)

library(vctrs)

x <- new_vctr(1:3, class = "foofy")
names(x) <- letters[1:3]
x
#> <foofy[3]>
#> a b c 
#> 1 2 3

z <- 1:3
names(z) <- letters[1:3]
z
#> a b c 
#> 1 2 3

f <- function(x) "blah"

sapply(x, f)
#> [1] "blah" "blah" "blah"
sapply(z, f)
#>      a      b      c 
#> "blah" "blah" "blah"

Why do the names of x not transfer, while those for z do? What's weird is that it seems to vary by the function being applied. Here both have names. (Well, it's slightly less surprising now that I see the inner/outer name thing I show below.)

g <- function(x) x

sapply(x, g)
#> a b c 
#> 1 2 3
sapply(z, g)
#> a b c 
#> 1 2 3

Looking at the source of sapply(), these seem to be important differences. The vctrs-made vector returns FALSE for is.vector(), which means it gets sent through as.list() and as.list() also does something quite different for x vs. z, in terms of inner vs outer names.

is.vector(x)
#> [1] FALSE
is.vector(z)
#> [1] TRUE

as.list(x)
#> [[1]]
#> <foofy[1]>
#> a 
#> 1 
#> 
#> [[2]]
#> <foofy[1]>
#> b 
#> 2 
#> 
#> [[3]]
#> <foofy[1]>
#> c 
#> 3
as.list(z)
#> $a
#> [1] 1
#> 
#> $b
#> [1] 2
#> 
#> $c
#> [1] 3

Created on 2021-07-19 by the reprex package (v2.0.0.9000)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions