New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default value to replace NULL #110

Closed
hadley opened this Issue Oct 22, 2015 · 20 comments

Comments

Projects
None yet
4 participants
@hadley
Member

hadley commented Oct 22, 2015

 issues %>% map(c("milestone", "title")) %>% map_chr(~ . %||% "")
@hadley

This comment has been minimized.

Member

hadley commented Oct 22, 2015

Maybe

 issues %>% map(c("milestone", "title")) %>% map_chr(empty = "")
@hadley

This comment has been minimized.

Member

hadley commented Nov 12, 2015

Or make it explicit

pluck <- function(y, default = NULL) {
  function(x) {
    x[[y]] %||% default
  }
}
issues %>% map_chr(pluck(c("milestone", "title"), ""))

Or

pluck <- function(..., default = NULL) {
  y <- c(...)
  function(x) {
    x[[y]] %||% default
  }
}

issues %>% map_chr(pluck("milestone", "title", default = ""))

But second form makes you think that pluck(1, "blah", 2) might work, and would be harder to call from as_function

@hadley hadley changed the title from How could we make this pattern simpler? to Default value to replace NULL Nov 14, 2015

@jennybc

This comment has been minimized.

Member

jennybc commented Nov 28, 2015

I think this is what this issue is about? Hope it's in the right place.

I'm trying to use a character vector as extractor function in map_chr(). All elements of the input list do indeed have appropriately named components but, in some cases, the value is NULL. This prevents the use of map_chr().

library(purrr)
x <- list(one = list(always = "a", sometimes = "b"),
          two = list(always = "b", sometimes = NULL))
x %>% map_chr("always")
#> one two 
#> "a" "b"
x %>% map_chr("sometimes")
#> Error in map_chr(., "sometimes"): Result 2 is not a length 1 character

This seems to get the job done for the time being:

x %>%
  map("sometimes") %>%
  map_if(is.null, ~ NA_character_) %>% 
  flatten_chr()
#> [1] "b" NA
@hadley

This comment has been minimized.

Member

hadley commented Dec 8, 2015

This is what it would look like with pluck():

library(purrr)
x <- list(
  one = list(always = "a", sometimes = "b"),
  two = list(always = "b", sometimes = NULL)
)
x %>% map_chr("always")
x %>% map_chr(pluck("sometimes", NA_character_))

But it should probably be an adverb:

hopefully <- function(.f, default = NULL) {
  .f <- as_function(.f)
  function(x) {
    .f(x) %||% default
  }
}
x <- list(
  one = list(always = "a", sometimes = "b"),
  two = list(always = "b", sometimes = NULL)
)
x %>% map_chr(hopefully("sometimes", NA_character_))

(But hopefully isn't the best name!)

@lionel-

This comment has been minimized.

Member

lionel- commented Dec 8, 2015

could be maybe() since we didn't use it for the other functions.
But it would be clearer if it mentioned NULL though.

unnull()? with_default()?

@tjmahr

This comment has been minimized.

Contributor

tjmahr commented Jan 5, 2016

I like null_with as an analogy to dplyr::failwith, but the name should follow the pattern set by safely, quietly, and possibly. If the metaphor is filling in missing values, then fully or completely (or filling/completing).

@jennybc

This comment has been minimized.

Member

jennybc commented Jan 6, 2016

This also feels a lot like data import and declaring how missing values present themselves, e.g. read.table(na.strings = ...) or readr::read_delim(na = ...). You could imagine map_*() and flatten_*() functions having something like that, with NULL being one of the things converted to NA by default.

An adverb feels a bit odd because it feels more like data is getting modified, than a function. Even though I do understand that when I say 'map: fetch me sometimes!', an extractor function is being applied under the hood.

@hadley

This comment has been minimized.

Member

hadley commented Jan 31, 2016

@jennybc hmmm, that's true and now that I think about it, there's no reason that map_chr(c("a", "b", "c"), default = NA) couldn't work. Do you have any thoughts on what the argument should be called? default? empty? null?

@hadley

This comment has been minimized.

Member

hadley commented Jan 31, 2016

And maybe map_chr(list("a", 1, "b", 2")) should also work

@lionel-

This comment has been minimized.

Member

lionel- commented Jan 31, 2016

I think .empty = NA is clear.

How do we signal we want to ignore NULLs and possibly get an output smaller than the input? .empty = NULL?

@hadley

This comment has been minimized.

Member

hadley commented Jan 31, 2016

Or missing?

@hadley

This comment has been minimized.

Member

hadley commented Jan 31, 2016

@lionel- I was thinking and realised empty isn't quite right because you're actually indexing into elements that don't exist (not just that they're empty). And the same constraints still apply to map - the output has to be the same length as the input.

@lionel-

This comment has been minimized.

Member

lionel- commented Jan 31, 2016

The problem with .missing is that it suggests it's a replacement for NA values. Maybe simply .null = NA?

@hadley

This comment has been minimized.

Member

hadley commented Jan 31, 2016

How about absent?

@lionel-

This comment has been minimized.

Member

lionel- commented Jan 31, 2016

I think absent is unnecessarily abstract. In comparison null is concrete and would be clearer and more evocative since all R programmers know what a NULL value is — or should read adv-r ;)

@hadley

This comment has been minimized.

Member

hadley commented Jan 31, 2016

But null is fundamentally misleading, because we're not replacing a NULL that exists, we're replacing a NULL that doesn't exist because we're indexing past the end of a list.

@tjmahr

This comment has been minimized.

Contributor

tjmahr commented Jan 31, 2016

@lionel-

This comment has been minimized.

Member

lionel- commented Jan 31, 2016

oooh I was confused. I thought it was about giving a default to actual NULL results instead of failing.

Since one peculiarity of the R language is that indexing absent values also yields NULL, I think it's still valid to call that argument .null.

mtcars$absent$absent
#> NULL

And then the argument .null can also serve the purpose of giving a default value for actual NULL results when .f is a function and not an index, which could be useful as well.

@lionel-

This comment has been minimized.

Member

lionel- commented Jan 31, 2016

@tjmahr I may like .default better actually :)

It would also work for actual NULL results.

@hadley

This comment has been minimized.

Member

hadley commented Jan 31, 2016

@lionel- the inconsistency is that mtcars[[12]] fails

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment