purrr icon indicating copy to clipboard operation
purrr copied to clipboard

map extractor of zero-length vector returns NULL

Open mmuurr opened this issue 4 years ago • 0 comments

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.

mmuurr avatar Jan 10 '21 07:01 mmuurr