Skip to content
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

clean_names() support for all types #483

Merged
merged 4 commits into from
May 16, 2022
Merged

clean_names() support for all types #483

merged 4 commits into from
May 16, 2022

Conversation

DanChaltiel
Copy link
Contributor

Description

Enhance clean_names.default() to support all types of objects as long as they have either names or dimnames.

Related Issue

fixes #481

Example

x = c("A%"=1, 2, 3)
names(x)
#> [1] "A%" ""   ""
janitor::clean_names(x)
#> a_percent         x       x_2 
#>         1         2         3

x = matrix(NA, nrow=2, ncol=2, dimnames=list(c("A B", "B C"), c("C D", "D E")))
janitor::clean_names(x)
#>     c_d d_e
#> a_b  NA  NA
#> b_c  NA  NA

x = 1:10
janitor::clean_names(x)
#> Error: `clean_names()` requires that either names or dimnames be non-null.

Created on 2022-05-15 by the reprex package (v2.0.1)

with some tests and a bullet in news
@billdenney
Copy link
Collaborator

billdenney commented May 15, 2022

Thanks for preparing this! While it's an edge case, could you please extend it so that it works with arbitrarily many dimnames (e.g. a 3D array like array(NA, dim = c(2, 2, 2), dimnames = list(c("A", "B"), c("C", "D"), c("E", "F"))))?

I would do that with lapply on the output of dimnames so that it is generic for as many dimnames as come out.

@codecov
Copy link

codecov bot commented May 15, 2022

Codecov Report

Merging #483 (da3630b) into main (ab90c1a) will increase coverage by 0.46%.
The diff coverage is 100.00%.

Impacted file tree graph

@@             Coverage Diff             @@
##             main      #483      +/-   ##
===========================================
+ Coverage   99.53%   100.00%   +0.46%     
===========================================
  Files          23        23              
  Lines        1084      1088       +4     
===========================================
+ Hits         1079      1088       +9     
+ Misses          5         0       -5     
Impacted Files Coverage Δ
R/clean_names.R 100.00% <100.00%> (+45.45%) ⬆️

@DanChaltiel
Copy link
Contributor Author

Good point indeed!
I never use arrays but I agree this should be as generic as possible.

I re-used your case as a test, testing that the output is lowercase should be enough. My other tests were maybe a bit overkill, as I'm re-testing make_clean_names() features, I could remove them if needed.

@billdenney
Copy link
Collaborator

With that one last change, I think we'd be good to merge. @sfirke any other things you see?

Copy link
Owner

@sfirke sfirke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! Thanks @DanChaltiel (and @billdenney for the review). Two comments that I think are minor re: testing, if those are addressed I'm good to merge 👍

tests/testthat/test-clean-names.R Outdated Show resolved Hide resolved
R/clean_names.R Outdated Show resolved Hide resolved
@DanChaltiel
Copy link
Contributor Author

There you are, this should do 👍

As a side comment, here is a quote from the book "Advanced R":

One of the challenges of error handling in R is that most functions generate one of the built-in conditions, which contain only a message and a call. That means that if you want to detect a specific type of error, you can only work with the text of the error message. This is error-prone, not only because the message might change over time, but also because messages can be translated into other languages.

Indeed, when you test your errors, you often rely on regexp. This means you cannot change the error/warning message as you might want. Since you already import rlang, you could swap every stop("foobar") to rlang::abort("foobar", class="..."), so that you can then call expect_error(expr, class="...") (and same for warnings).
I did that in my package crosstable a while ago and found this refactor was really worth it.

@billdenney
Copy link
Collaborator

@DanChaltiel , I wish that all messages, warnings, and errors were classed, so that testing would be better, as you suggest. I've started using classed messages, warnings, and errors some of the time in my code. It is helpful. If you add classes, please make the class name start with the package name so that there is a near-zero chance of conflicts.

@sfirke
Copy link
Owner

sfirke commented May 16, 2022

Good idea on the classing of errors, Dan. Thanks for this PR and for being receptive to tweaks! I'm merging it now.

@sfirke sfirke merged commit 0029c27 into sfirke:main May 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

FR: support all classes by default in clean_names()
3 participants