Tip: alternatives to paste
# ok
paste(letters[1:10], collapse = ", ")
# "a, b, c, d, e, f, g, h, i, j"
# shorter
toString(letters[1:10])
# "a, b, c, d, e, f, g, h, i, j"
# bonus point: how to literally print a list
some_list <- list(a = 1, b = 2, list(c = 10))
toString(list(some_list))
# bonus point: how to limit the number of printed elements
R.utils::hpaste(letters[1:10])
# [1] "a, b, c, ..., j"
R.utils::hpaste(letters[1:10], maxHead = 2, maxTail = 3, lastCollapse = " and ")
# [1] "a, b, ..., h, i and j"
Tip: take care when using the ifelse function
# ifelse return logical(0) when test equals logical(0)
ifelse(
test = NULL == 4,
yes = "yes",
no = "no"
)
# logical(0)
# ifelse is not vectorized
ifelse(
test = TRUE,
yes = c("yes", "maybe"),
no = "no"
)
# "yes"
# compare with what happens when using data.table::fifelse instead:
data.table::fifelse(
test = TRUE,
yes = c("yes", "maybe"),
no = "no"
)
# Error in data.table::fifelse(test = TRUE, yes = c("yes", "maybe"), no = "no") :
# Length of 'yes' is 2 but must be 1 or length of 'test' (1).
data.table::fifelse(
test = c(TRUE, FALSE),
yes = c("yes", "maybe"),
no = "no"
)
# "yes" "no"
If you are just looking for a shorter way of writing if/else, maybe you already considered using the ternary operator:
# example from the documentation page of the ternary operators
library(rtern)
out <- c(4, 2) < 3 ? "it_was_true" : "it_was_false"
str(out)
# chr [1:2] "it_was_false" "it_was_true"
out <- c(FALSE, TRUE) ? "it_was_true" : "it_was_false"
str(out)
c(TRUE, FALSE) ? "yes" : "no"
# [1] "yes" "no"
# other examples
NULL == 4 ? "yes" : "no"
# Error in rhss[[2 - as.numeric(lhs)]] :
# attempt to select less than one element in get1index
TRUE ? c("yes", "maybe") : "no"
# "yes" "maybe"
TRUE ? c("yes", "maybe") : c("no", "really no")
# "yes" "maybe"
Unfortunately this operator also shows some unexpected results.
out <- c(TRUE, FALSE) ? c("yes", "maybe") : c("no", "really no")
str(out)
# List of 2
# $ : chr "yes"
# $ : language c("no", "really no")
As usual, make sure you test your code at least for some trivial cases, to make sure that it does what you want...
Tip: avoid using the paste function when not mandatory
# https://github.com/facebook/prophet/blob/main/R/R/diagnostics.R
# ok
stop(
paste(
'Less data than horizon after initial window.',
'Make horizon or initial shorter.'
)
)
# better
# note that you need to add empty spaces between the 2 strings
stop(
'Less data than horizon after initial window.',
' Make horizon or initial shorter.'
)
Tip: in if/else statemens emphasize the main action, not the final result
I noticed a tendency to concentrate on the result, and not on the actual thing that is actually checked in if/else statements. To explain what I mean, here is an imaginary example:
if (user %in% superusers) {
curl::send_mail(
mail_from = "somebody",
mail_rcpt = user,
message = paste("You'll get a raise of", secret_raise)
)
} else {
curl::send_mail(
mail_from = "somebody",
mail_rcpt = user,
message = "No raise for you"
)
}
It takes a few seconds for the reader to realize that the purpose of the if/else statements is to set the message based on user. I would rewrite the code to make this obvious:
msg <- "No raise for you"
if (user %in% superusers)
{
msg <- paste("You'll get a raise of", secret_raise)
}
curl::send_mail(
mail_from = "somebody",
mail_rcpt = user,
message = msg
)
Less code and easier to read/follow. Just in case you dont't believe this way of writing code is not seen often, here a real example:
# https://github.com/rstudio/gt/blob/master/R/dt_boxhead.R
# ok
if (is.list(dt_boxhead[[names(val_list)]])) {
dt_boxhead[[which(dt_boxhead$var == var_name), names(val_list)]] <- unname(val_list)
} else {
dt_boxhead[[which(dt_boxhead$var == var_name), names(val_list)]] <- unlist(val_list)
}
# better (I admit, the variable name can be improved)
new_value <- unlist(val_list)
if (is.list(dt_boxhead[[names(val_list)]])) {
new_value <- unname(val_list)
}
dt_boxhead[[which(dt_boxhead$var == var_name), names(val_list)]] <- new_value
Did you find anything from above useful?
When trying to explain things, I often realize that I don't have the level of a person like Feynman when explaining why ice is slippery...
In case you knew all this:
https://sketchplanations.com/the-rule-of-7
My father used to say: "do something good for the people you meet - or leave them alone". I now leave you alone.
Make a promise. Show up. Do the work. Repeat.