This is likely a bug in case_when
or its internal helper functions.
We can put a browser
in the source code of case_when
to see what happens in both cases. Some internal functions must be called via :::
.
f <- function (...) {
browser()
fs <- dplyr:::compact_null(rlang::list2(...))
n <- length(fs)
error_call <- rlang::current_env()
if (n == 0) {
abort("No cases provided.", call = error_call)
}
query <- vector("list", n)
value <- vector("list", n)
default_env <- rlang::caller_env()
quos_pairs <- purrr::map2(fs, seq_along(fs), dplyr:::validate_formula, default_env = default_env,
dots_env = rlang::current_env(), error_call = error_call)
for (i in seq_len(n)) {
pair <- quos_pairs[[i]]
query[[i]] <- rlang::eval_tidy(pair$lhs, env = default_env)
value[[i]] <- rlang::eval_tidy(pair$rhs, env = default_env)
if (!is.logical(query[[i]])) {
dplyr:::abort_case_when_logical(pair$lhs, i, query[[i]],
error_call = error_call)
}
}
m <- dplyr:::validate_case_when_length(query, value, fs, error_call = error_call)
out <- value[[1]][rep(NA_integer_, m)]
replaced <- rep(FALSE, m)
for (i in seq_len(n)) {
out <- dplyr:::replace_with(out, query[[i]] & !replaced, value[[i]],
NULL, error_call = error_call)
replaced <- replaced | (query[[i]] & !is.na(query[[i]]))
}
out
}
and the helper internal replace_with
in dplyr
,
replacer <- function (x, i, val, name, reason = NULL, error_call = rlang::caller_env()) {
if (is.null(val)) {
return(x)
}
dplyr:::check_length(val, x, name, reason, error_call = error_call)
dplyr:::check_type(val, x, name, error_call = error_call)
dplyr:::check_class(val, x, name, error_call = error_call)
i[is.na(i)] <- FALSE
if (length(val) == 1L) {
x[i] <- val
}
else {
x[i] <- val[i]
}
x
}
and then debug via
x <- character(0)
f(rlang::is_empty(x) ~ "Empty", !rlang::is_empty(x) ~ "x")
f(rlang::is_empty(x) ~ "Empty", !rlang::is_empty(x) ~ x)
the key is in value m
, that in the working case results in 1L
, in the faulty case it is 0L
. out
becomes character(0)
instead of initializing to NA
, length 1.
replaced
should be a logical vector indicating whether a value has been replaced. In the faulty case rep(FALSE, 0L)
is logical(0)
, which is queried later on via !replaced
. FALSE & logical(0)
gives logical(0)
.
When passed to replacer
this gives a peculiar subsetting action character(0)[logical(0)]
, that gives character(0)
.