purrr icon indicating copy to clipboard operation
purrr copied to clipboard

Equivalent of has_element() for element names in a list?

Open jameslairdsmith opened this issue 5 years ago • 4 comments

I'm wondering if there is a purrr function that detects whether an element with a given name appears in a list. For example, in the following code:

> library(purrr)
> ingredients <- list(tomatoes = 3, radishes = 5, cucumbers = 2)

I'm looking for a simple way to find out if the ingredients list contains "tomatoes".

I tried the has_element() function, but it only works with the values in a list, not the names:

> has_element(ingredients, "tomatoes")
[1] FALSE

> has_element(ingredients, 3)
[1] TRUE

This makes sense to me as you probably want to avoid the situation where you accidentally match a name in the list when you are looking to match a value instead.

I ended up writing a function like this:

has_element_named <- function(list, name){!is.null(list[[name]])}

> has_element_named(ingredients, "tomatoes")
[1] TRUE

> has_element_named(ingredients, "celery")
[1] FALSE

But this runs the same risk: that it will also match a value as well as the name:

> has_element_named(ingredients, 3)
[1] TRUE

This might be desirable in some circumstances, but most people will probably want to match against either names or values, not both. So I wrote:

has_element_called <- function(list, name){name %in% names(list)}

> has_element_called(ingredients, "tomatoes")
[1] TRUE
> has_element_called(ingredients, "celery")
[1] FALSE
> has_element_called(ingredients, 3)
[1] FALSE

Which is simple enough to do I guess, but there may be a better designed or robust function that could be written (or maybe it already exists I just can't find it). This function doesn't, for example, account for any depth in the list you are matching against.

jameslairdsmith avatar Nov 03 '18 16:11 jameslairdsmith

FWIW rlang::has_name() does this.

ingredients <- list(tomatoes = 3, radishes = 5, cucumbers = 2)

rlang::has_name(ingredients, "radishes")
#> [1] TRUE

rlang::has_name(ingredients, "onions")
#> [1] FALSE

Created on 2018-11-03 by the reprex package (v0.2.1)

jennybc avatar Nov 03 '18 17:11 jennybc

And for checking a position exists, I would use length(x) >= n.

lionel- avatar Nov 14 '18 14:11 lionel-

This is basically the same as can_pluck()

hadley avatar Feb 25 '19 17:02 hadley

Is there any way to look for an element name that is nested deeper in lists of lists?

jzadra avatar Jan 27 '22 23:01 jzadra

Might be worth generalising to has_index(x, idx) which would if idx was integer, character, or list (i.e. recursively).

hadley avatar Aug 24 '22 09:08 hadley

Maybe is_pluckable()? Or pluck_exists()?

hadley avatar Aug 27 '22 19:08 hadley