Readable code with base R (part 2)

base R coding style

It’s been a while since my first on this topic. Nevertheless, it remains an important issue as (in my humble view) there is still too much code appearing in R packages that lacks good readability. So I hope this post helps to promote the beauty of readable code.


Combine startsWith and endsWith with R’s pipe

In my first post, startsWith and endsWith were presented. In combination with R’s pipe operator, we can improve the readability even further.

w <- "Hello World!"

w |> startsWith("Hell")
[1] TRUE
w |> endsWith("!")
[1] TRUE

Proceeding with the example of the initial post, let’s see this in context of control flow.

tell_file_type <- function(filename)
{
    if (filename |> endsWith("txt"))
        print("A text file.")
    
    excel_endings <- c("xlsx", "xls")
    
    if (any(filename |> endsWith(excel_endings)))
        print("An Excel file.")
    
}
tell_file_type("A.xlsx")
[1] "An Excel file."

%in% and %not in%

The %in% operator is commonly used. To improve the readability of something like

existing_names <- c("Lisa", "Bob")

name <- "Peter"

hasNewName = !(name %in% existing_names)

you can always define your own operators.

`%not in%` <- Negate(`%in%`)

hasNewName = name %not in% existing_names

Obviously, the readability also depends on the choice of proper variable names. My general take on this: don’t shy away from longish variable names, if it improves code readability.

In this context, it is often useful to assign boolean values to variables. For example, instead of

if (abs_error < 1e-8) {
    # ...
}

you should do

hasConverged <- abs_error < 1e-8

if (hasConverged) {
    # ...
}

That is, it is ok to add redundancies in your code if it improves readability.

Sometimes, it is not immediately clear from the naming of base R functions, what they do. Of course, you are free to redefine them with a proper name.

equals_pattern = function(x, pattern, ...) grepl(pattern, x, ...)

x <- "Peter"

x |> equals_pattern("^P")
[1] TRUE

Lastly, let’s combine all of the above.

x |> equals_pattern("^P") && 
x |> startsWith("z")      &&
x %not in% existing_names 
[1] FALSE

That’s it for now. Some of the above examples may or may not appear a bit artificial, which can happen with examples, I guess. Let me know in the comments how you think about this topic or maybe you have another good example of readable code.

Footnotes

    Reuse

    Text and figures are licensed under Creative Commons Attribution CC BY-SA 4.0. The figures that have been reused from other sources don't fall under this license and can be recognized by a note in their caption: "Figure from ...".