Site icon R-bloggers

Useful functions for dealing with object names

[This article was first published on Maëlle's R blog on Maëlle Salmon's personal website, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

My sticky note filled up quickly after I only added setNames() on it, with related functions for dealing with object names, in base R and beyond!

(Un)Setting object names: stats::setNames(), unname() and rlang::set_names()

I noticed a function ending with something like this:

blop <- function() {
  
  # code creating the df data.frame
  # ...
  
  names(df) <- c("col1", "col2")
  df
}

It struck me as simplifiable by:

blop <- function() {
  
  # code creating the df data.frame
  # ...
  
  stats::setNames(df, c("col1", "col2"))
}

Interestingly the docs for setNames() sound as if it were created just for this use case!

“This is a convenience function that sets the names on an object and returns the object. It is most useful at the end of a function definition where one is creating the object to be returned and would prefer not to store it under a name just so the names can be assigned.”

For the opposite operation, removing the names of an object, we can use unname().

Unsurprisingly, the rlang package has its own special rlang::set_names(), that is more or less a stats::setNames() + unname() + generic janitor::clean_names() combo!

(df <- data.frame(c(1, 2), c(5, 7)))
#>   c.1..2. c.5..7.
#> 1       1       5
#> 2       2       7

rlang::set_names(df, c("a", "b")) # sets names!
#>   a b
#> 1 1 5
#> 2 2 7

rlang::set_names(df, NULL) # removes names
#>      
#> 1 1 5
#> 2 2 7

df2 <- rlang::set_names(df, c("SHOUTING", "LOUDLY"))
df2
#>   SHOUTING LOUDLY
#> 1        1      5
#> 2        2      7
rlang::set_names(df2, tolower) # using a function to transform names
#>   shouting loudly
#> 1        1      5
#> 2        2      7

Of note,

set_names() is stable and exported in purrr”.

So if you prefer, you can use purrr::set_names() instead.

Last but not least, despite the examples above being on a data.frame, these functions can be used on lists, vectors…

Extracting names: names(), rlang::names2()

names() probably doesn’t need any introduction, but it’s interesting to mention it in contrast to rlang::names2() that " always returns a character vector, even when an object does not have a names attribute.”.

vector <- c(1, 2)
names(vector)
#> NULL
rlang::names2(vector)
#> [1] "" ""

rlang also has a replacement variant, rlang::names2<-, that “never adds NA names and instead fills unnamed vectors with "".”

I didn’t know all this before skimming the list of rlang functions dealing with object attributes.

Checking names

Is the object named: rlang::is_named(), rlang::is_named2(), rlang::have_name()

rlang::have_name() is the vectorized version of rlang::is_name(), not to be confused with rlang::has_name(). 😅

rlang::is_named(c(a = 1))
#> [1] TRUE

rlang::is_named(NULL)
#> [1] FALSE
rlang::is_named2(NULL)
#> [1] TRUE

rlang::have_name(c(a = 1, 2)) # the example from the manual page
#> [1]  TRUE FALSE

I do not know of a base R equivalent, I mean I’d find a way to express it, but not as directly. But maybe I am missing yet another base R gem!

Does the object have an element called X, does the object have elements called X, Y: utils::hasName(), rlang::has_name()

If you want to check that an object contains elements with certain names, you can use utils::hasName()

expected_names <- c("pof", "blop")

vector1 <- c(pof = 1, bla = 2)
vector2 <- c(pof = 1, bla = 2, blop = 3)

utils::hasName(vector1, expected_names)
#> [1]  TRUE FALSE
utils::hasName(vector2, expected_names)
#> [1] TRUE TRUE

The rlang package has a similar function, rlang::has_name().

expected_names <- c("pof", "blop")

vector1 <- c(pof = 1, bla = 2)
vector2 <- c(pof = 1, bla = 2, blop = 3)

rlang::has_name(vector1, expected_names)
#> [1]  TRUE FALSE
rlang::has_name(vector2, expected_names)
#> [1] TRUE TRUE

I’m honestly not sure whether there’s any difference between the utils and rlang versions, apart from the name!

In package tests: testthat::expect_named()

testthat::expect_named() allows you to check that an object returned by your code has the names you expect, or simply has names. You can ignore case and order based on the arguments ignore.case and ignore.order.

(object <- tibble::tibble(a = c(1, 2), b = c(3, 4)))
#> # A tibble: 2 × 2
#>       a     b
#>   <dbl> <dbl>
#> 1     1     3
#> 2     2     4

testthat::expect_named(object)

unnamed_object <- unname(object)
testthat::expect_named(unnamed_object)
#> Error: `unnamed_object` does not have names.

testthat::expect_named(object, c("b", "a"))
#> Error: Names of `object` ('a', 'b') don't match 'b', 'a'
testthat::expect_named(object, c("b", "a"), ignore.order = TRUE)

testthat::expect_named(object, c("A", "B"))
#> Error: Names of `object` ('a', 'b') don't match 'A', 'B'
testthat::expect_named(object, c("A", "B"), ignore.case = TRUE)

If you’re not used to using this expectation yet, lintr can help you a bit.

Conclusion

In this post I went through functions that deals with names: stats::setNames(), unname() and rlang::set_names() for (un)setting names (my initial motivation for this post!); names(), rlang::names2() for extracting names; rlang::is_named(), rlang::is_named2() and rlang::have_name() to find out whether an object is named; utils::hasName() and rlang::has_name() to find out whether an object contains elements of a given name; testthat::expect_named() for testing whether an object has names, or specific names. Any other related function that comes to mind?

To leave a comment for the author, please follow the link and comment on their blog: Maëlle's R blog on Maëlle Salmon's personal website.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Exit mobile version