#' Like `paste()`, but missing values are omitted
#'
#' @details If all values are missing, the value from the first argument is
#' preserved.
#'
#' @param ...,sep,collapse See [base::paste()]
#' @return A character vector of pasted values.
#' @examples
#' paste_skip_na(NA) # NA_character_
#' paste_skip_na("A", NA) # "A"
#' paste_skip_na("A", NA, c(NA, "B"), sep = ",") # c("A", "A,B")
#' @export
paste_skip_na <- function(..., sep = " ", collapse = NULL) {
args <- list(...)
if (length(args) <= 1) {
if (length(args) == 0) {
# match the behavior of paste
paste(sep = sep, collapse = collapse)
} else if (!is.null(collapse)) {
if (all(is.na(args[[1]]))) {
# Collapsing with all NA values results in NA
NA_character_
} else {
# Collapsing without all NA values collapses the non-NA values
paste(na.omit(args[[1]]), sep = sep, collapse = collapse)
}
} else {
# as.character() to ensure that logical NA values are converted to
# NA_character_
as.character(args[[1]])
}
} else {
# There are at least 2 arguments; paste the first two and recurse
a1 <- args[[1]]
a2 <- args[[2]]
if (length(a1) != length(a2)) {
if (length(a1) == 1) {
a1 <- rep(a1, length(a2))
} else if (length(a2) == 1) {
a2 <- rep(a2, length(a1))
} else {
stop("Arguments must be the same length or one argument must be a scalar.")
}
}
# Which arguments are NA, if any?
mask1 <- !is.na(a1)
mask2 <- !is.na(a2)
mask_both <- mask1 & mask2
mask_only2 <- (!mask1) & mask2
firsttwo <- a1
if (any(mask_only2)) {
firsttwo[mask_only2] <- a2[mask_only2]
}
if (any(mask_both)) {
# Collapse only occurs on the final pasting
firsttwo[mask_both] <- paste(a1[mask_both], a2[mask_both], sep = sep, collapse = NULL)
}
# prepare to recurse, and recurse
new_args <- append(list(firsttwo), args[-(1:2)])
new_args$sep <- sep
new_args$collapse <- collapse
do.call(paste_skip_na, new_args)
}
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.