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

sketchplanations the rule of seven

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.