purrr
purrr copied to clipboard
map extractor of zero-length vector returns NULL
map
(and plenty of other related functions, as described in #480) converts typed zero-length vectors to NULL
:
map(list(a = integer(0)), "a")
# NULL
I realize this is actually documented behavior in as_mapper
, but when combined with other tidyverse functions that rely on vctrs, this causes headaches. Dropping the type data creates various inconsistencies: e.g. a list (fetched from a data package or external source) that has typed data, but that data might be empty (i.e. 0-length) in some cases. Consider a package wrapping an external API and casting the results to be type-safe with other tidyverse functions (e.g. bind_rows
applied to the data from multiple such API calls):
## list data fetched from external source; empty data but typed:
my_list <- list(
a = list(desc = "some numeric data", data = numeric(0)), ## may not always be empty
b = list(desc = "here are labels", data = character(0)) ## may not always be empty
)
## let's plop that data into a tibble:
my_list %>% map("data") %>% as_tibble() ## errs
# Error: All columns in a tibble must be vectors.
# ✖ Column `a` is NULL.
# ✖ Column `b` is NULL.
# Run `rlang::last_error()` to see where the error occurred.
## but wait ... this succeeds (and correctly preserves types):
as_tibble(transpose(my_list)$data)
# # A tibble: 0 x 2
# # … with 2 variables: a <int>, b <chr>
In this case, the upstream data source has already done all the hard work of validating data and appropriately casting those data, and downstream code may not know a priori all the cols/fields, so adding .default
and specifying each column doesn't easily apply.
I don't know how much of this conversation is relevant to just purrr, but in general I think empty-vectors' types should be preserved (when present), since identical(integer(), NULL)
is FALSE
.