Nothing
# `case_when()` ----------------------------------------------------------------
test_that("matches values in order", {
x <- 1:3
expect_equal(
case_when(
x <= 1 ~ 1,
x <= 2 ~ 2,
x <= 3 ~ 3
),
c(1, 2, 3)
)
})
test_that("unmatched gets missing value", {
x <- 1:3
expect_equal(
case_when(
x <= 1 ~ 1,
x <= 2 ~ 2
),
c(1, 2, NA)
)
})
test_that("missing values can be replaced (#1999)", {
x <- c(1:3, NA)
expect_equal(
case_when(
x <= 1 ~ 1,
x <= 2 ~ 2,
is.na(x) ~ 0
),
c(1, 2, NA, 0)
)
})
test_that("NA conditions (#2927)", {
expect_equal(
case_when(
c(TRUE, FALSE, NA) ~ 1:3,
TRUE ~ 4L
),
c(1L, 4L, 4L)
)
})
test_that("any `TRUE` overrides an `NA`", {
x <- c(1, 2, NA, 3)
expect <- c("one", "not_one", "missing", "not_one")
# `TRUE` overriding before the `NA`
expect_identical(
case_when(
is.na(x) ~ "missing",
x == 1 ~ "one",
.default = "not_one"
),
expect
)
# `TRUE` overriding after the `NA`
expect_identical(
case_when(
x == 1 ~ "one",
is.na(x) ~ "missing",
.default = "not_one"
),
expect
)
})
test_that("case_when can be used in anonymous functions (#3422)", {
res <- tibble(a = 1:3) |>
mutate(b = (function(x) case_when(x < 2 ~ TRUE, .default = FALSE))(a)) |>
pull()
expect_equal(res, c(TRUE, FALSE, FALSE))
})
test_that("case_when() can be used inside mutate()", {
out <- mtcars[1:4, ] |>
mutate(
out = case_when(
cyl == 4 ~ 1,
.data[["am"]] == 1 ~ 2,
.default = 0
)
) |>
pull()
expect_identical(out, c(2, 2, 1, 0))
})
test_that("case_when() conditions must be logical (and aren't cast to logical!)", {
expect_snapshot(error = TRUE, {
case_when(1 ~ 2)
})
# Make sure input numbering is right in the error message!
expect_snapshot(error = TRUE, {
case_when(TRUE ~ 2, 3.5 ~ 4)
})
})
test_that("case_when() accepts logical condition vectors with attributes (#6678)", {
x <- structure(c(FALSE, TRUE), label = "foo")
expect_identical(case_when(x ~ 1, .default = 2), c(2, 1))
})
test_that("case_when() does not accept classed logical conditions", {
# From a vctrs perspective, these aren't "logical condition indices"
x <- structure(c(FALSE, TRUE), class = "my_logical")
expect_snapshot(error = TRUE, {
case_when(x ~ 1)
})
})
test_that("case_when() logical conditions can't be arrays (#6862)", {
x <- array(TRUE, dim = c(3, 3))
y <- c("a", "b", "c")
expect_snapshot(error = TRUE, {
case_when(x ~ y)
})
# Not even 1D arrays
x <- array(TRUE, dim = 3)
expect_snapshot(error = TRUE, {
case_when(x ~ y)
})
})
test_that("can pass quosures to case_when()", {
fs <- local({
x <- 3:1
quos(
x < 2 ~ TRUE,
TRUE ~ FALSE
)
})
expect_identical(case_when(!!!fs), c(FALSE, FALSE, TRUE))
})
test_that("can pass nested quosures to case_when()", {
fs <- local({
foo <- mtcars$cyl[1:4]
quos(
!!quo(foo) == 4 ~ 1,
TRUE ~ 0
)
})
expect_identical(case_when(!!!fs), c(0, 0, 1, 0))
})
test_that("can pass unevaluated formulas to case_when()", {
x <- 6:8
fs <- exprs(
x == 7L ~ TRUE,
TRUE ~ FALSE
)
expect_identical(case_when(!!!fs), c(FALSE, TRUE, FALSE))
out <- local({
x <- 7:9
case_when(!!!fs)
})
expect_identical(out, c(TRUE, FALSE, FALSE))
})
test_that("unevaluated formulas can refer to data mask", {
fs <- exprs(
cyl == 4 ~ 1,
am == 1 ~ 2,
TRUE ~ 0
)
out <- mtcars[1:4, ] |> mutate(out = case_when(!!!fs)) |> pull()
expect_identical(out, c(2, 2, 1, 0))
})
test_that("unevaluated formulas can contain quosures", {
quo <- local({
n <- 4
quo(n)
})
fs <- exprs(
cyl == !!quo ~ 1,
am == 1 ~ 2,
TRUE ~ 0
)
out <- mtcars[1:4, ] |> mutate(out = case_when(!!!fs)) |> pull()
expect_identical(out, c(2, 2, 1, 0))
})
test_that("NULL inputs are compacted", {
x <- 1:3
bool <- FALSE
out <- case_when(
x == 2 ~ TRUE,
if (bool) x == 3 ~ NA,
.default = FALSE
)
expect_identical(out, c(FALSE, TRUE, FALSE))
bool <- TRUE
out <- case_when(
x == 2 ~ TRUE,
if (bool) x == 3 ~ NA,
.default = FALSE
)
expect_identical(out, c(FALSE, TRUE, NA))
})
test_that("passes through `.default` correctly", {
expect_identical(case_when(FALSE ~ 1, .default = 2), 2)
expect_identical(
case_when(c(TRUE, FALSE, TRUE, FALSE, TRUE) ~ 1:5, .default = 2),
c(1, 2, 3, 2, 5)
)
})
test_that("`.default` isn't part of recycling", {
# Because eventually we want to only take the output size from the LHS conditions,
# so having `.default` participate in the common size is a step in the wrong
# direction
expect_snapshot(error = TRUE, {
case_when(FALSE ~ 1L, .default = 2:5)
})
})
test_that("`.default` is part of common type computation", {
expect_identical(case_when(TRUE ~ 1L, .default = 2), 1)
expect_snapshot(error = TRUE, {
case_when(TRUE ~ 1L, .default = "x")
})
})
test_that("passes through `.ptype` correctly", {
expect_identical(case_when(TRUE ~ 1, .ptype = integer()), 1L)
expect_snapshot(error = TRUE, {
case_when(TRUE ~ 1, FALSE ~ 1.5, .ptype = integer())
})
# Error index is right when `NULL` is involved
expect_snapshot(error = TRUE, {
case_when(TRUE ~ 1, NULL, FALSE ~ 1.5, .ptype = integer())
})
})
test_that("passes through `.size` correctly", {
expect_identical(case_when(TRUE ~ 1, .size = 2), c(1, 1))
expect_snapshot(error = TRUE, {
case_when(TRUE ~ 1:2, .size = 3)
})
# Error index is right when `NULL` is involved
expect_snapshot(error = TRUE, {
case_when(TRUE ~ 1:3, NULL, TRUE ~ 1:2, .size = 3)
})
})
test_that("can't supply `.default` and `.unmatched`", {
# Probably overkill to add `unmatched_arg` just to get `.unmatched` instead
# of `unmatched`.
expect_snapshot(error = TRUE, {
case_when(TRUE ~ 1, .default = 1, .unmatched = "error")
})
})
test_that("`.unmatched` is validated", {
# Probably overkill to add `unmatched_arg` to `vec_case_when()` just to get
# `.unmatched` instead of `unmatched`
expect_snapshot(error = TRUE, {
case_when(TRUE ~ 1, .unmatched = "foo")
})
expect_snapshot(error = TRUE, {
case_when(TRUE ~ 1, .unmatched = 1)
})
})
test_that("`.unmatched` treats `FALSE` like an unmatched location", {
expect_snapshot(error = TRUE, {
case_when(
c(TRUE, FALSE, TRUE) ~ 1,
.unmatched = "error"
)
})
})
test_that("`.unmatched` treats `NA` like an unmatched location", {
expect_snapshot(error = TRUE, {
case_when(
c(TRUE, NA, TRUE) ~ 1,
.unmatched = "error"
)
})
})
test_that("`.unmatched` errors pluralize well", {
# One location
x <- letters[1:5]
expect_snapshot(error = TRUE, {
case_when(
x == "a" ~ 1,
x == "b" ~ 2,
x == "c" ~ 3,
x == "e" ~ 4,
.unmatched = "error"
)
})
# Two locations
x <- letters[1:5]
expect_snapshot(error = TRUE, {
case_when(
x == "a" ~ 1,
x == "c" ~ 2,
x == "e" ~ 3,
.unmatched = "error"
)
})
# Many locations
x <- 1:100
expect_snapshot(error = TRUE, {
case_when(x == 1 ~ "a", .unmatched = "error")
})
})
# `case_when()` errors ---------------------------------------------------------
test_that("invalid type errors are correct (#6261) (#6206)", {
expect_snapshot(error = TRUE, {
case_when(TRUE ~ 1, TRUE ~ "x")
})
})
test_that("`NULL` formula element throws meaningful error (#7794)", {
# "Must be a vector" errors
expect_snapshot(error = TRUE, {
case_when(NULL ~ NULL)
case_when(TRUE ~ NULL)
case_when(NULL ~ TRUE)
case_when(c(TRUE, TRUE) ~ NULL)
case_when(NULL ~ c(TRUE, TRUE))
case_when(TRUE ~ NULL, c(TRUE, TRUE) ~ NULL)
case_when(NULL ~ TRUE, NULL ~ c(TRUE, TRUE))
})
# Recycling errors come first
expect_snapshot(error = TRUE, {
case_when(c(TRUE, TRUE) ~ NULL, c(TRUE, TRUE, TRUE) ~ NULL)
case_when(NULL ~ c(TRUE, TRUE), NULL ~ c(TRUE, TRUE, TRUE))
})
})
test_that("throws chained errors when formula evaluation fails", {
expect_snapshot(error = TRUE, {
case_when(1 ~ 2, 3 ~ stop("oh no!"))
})
expect_snapshot(error = TRUE, {
case_when(1 ~ 2, stop("oh no!") ~ 4)
})
})
test_that("case_when() give meaningful errors", {
expect_snapshot({
(expect_error(
case_when(
c(TRUE, FALSE) ~ 1:3,
c(FALSE, TRUE) ~ 1:2
)
))
(expect_error(
case_when(
c(TRUE, FALSE) ~ 1,
c(FALSE, TRUE, FALSE) ~ 2,
c(FALSE, TRUE, FALSE, NA) ~ 3
)
))
(expect_error(
case_when(51:53 ~ 1:3)
))
(expect_error(
case_when(paste(50))
))
(expect_error(
case_when(y ~ x, paste(50))
))
(expect_error(
case_when()
))
(expect_error(
case_when(NULL)
))
(expect_error(
case_when(~ 1:2)
))
})
})
# `case_when()` deprecated -----------------------------------------------------
test_that("Using scalar LHS with vector RHS is deprecated (#7082)", {
# In many packages, people use `case_when()` when they should be using a
# series of if statements. We try to warn when we detect this.
expect_snapshot({
# Columns
x <- 1:5
y <- 6:10
# Scalars
code <- 1L
sex <- "M"
# This is really a series of if statements.
# This is highly inefficient because each scalar LHS is recycled to size 5.
expect_identical(
case_when(
code == 1L && sex == "M" ~ x,
code == 1L && sex == "F" ~ y,
code == 1L && sex == "M" ~ x + 1L,
.default = 0L
),
x
)
})
# Motivating example of a silent bug that results from allowing this kind of
# common size determination (#7082). We ideally want this case to fail. LHS
# common size is 1 and RHS inputs ideally should be forced to recycle to this
# size. Since both the LHS and RHS inputs are consulted to compute a common
# size of 0, this incorrectly returns `character()`, but we at least warn the
# user that something is fishy here, and hopefully they take a closer look and
# catch their error.
expect_snapshot({
x <- 1
case_when(
x == 1 ~ "a",
x == 2 ~ character(),
.default = "other"
)
})
# Now confirm that the other 3 possible combinations don't warn!
# size 1 LHS, size 1 RHS
expect_identical(
expect_no_warning(case_when(TRUE ~ "a", FALSE ~ "b")),
"a"
)
# size >1 LHS, size 1 RHS
expect_identical(
expect_no_warning(case_when(c(TRUE, FALSE) ~ "a", c(FALSE, TRUE) ~ "b")),
c("a", "b")
)
# size >1 LHS, size >1 RHS
expect_identical(
expect_no_warning(case_when(
c(TRUE, FALSE) ~ c("a", "b"),
c(FALSE, TRUE) ~ c("c", "d")
)),
c("a", "d")
)
})
# `replace_when()` -------------------------------------------------------------
test_that("replace_when() recycles scalar RHS", {
x <- c(1, 2, 3, 1, 2, 3)
expect_identical(
replace_when(x, x == 1 ~ 0, x == 3 ~ 4),
c(0, 2, 4, 0, 2, 4)
)
})
test_that("replace_when() allows vector RHS of the same size as `x`", {
x <- c(1, 2, 3, 1, 2, 3)
y <- seq_along(x)
expect_identical(
replace_when(x, x == 1 ~ 0, x == 3 ~ y),
c(0, 2, 3, 0, 2, 6)
)
expect_snapshot(error = TRUE, {
replace_when(x, x == 1 ~ 1:3)
})
})
test_that("replace_when() does not recycle LHS values", {
# Unlike `case_when()` we get to do this right!
x <- c(1, 2, 3)
expect_snapshot(error = TRUE, {
replace_when(x, TRUE ~ 0)
})
# Error index is right when `NULL` is involved
expect_snapshot(error = TRUE, {
replace_when(x, c(TRUE, TRUE, TRUE) ~ 0, NULL, TRUE ~ 0)
})
})
test_that("replace_when() retains the type of `x`", {
x <- c(1L, 2L)
# Not going towards common type of double
expect_identical(
replace_when(x, x == 1L ~ 0),
c(0L, 2L)
)
x <- factor(c("a", "b", "c"))
# Note common type would be character
expect_identical(
replace_when(x, x == "a" ~ "c"),
factor(c("c", "b", "c"), levels = c("a", "b", "c"))
)
# Can't cast to unknown level
expect_snapshot(error = TRUE, {
replace_when(x, x == "a" ~ "d")
})
# Error index is right when `NULL` is involved
expect_snapshot(error = TRUE, {
replace_when(x, x == "a" ~ "b", NULL, x == "b" ~ "d")
})
})
test_that("replace_when() retains names of `x`, consistent with `base::replace()`", {
x <- c(a = 1, b = 2, c = 3)
expect_identical(
replace_when(
x,
x == 1 ~ 0,
x == 3 ~ c(z = 4)
),
c(a = 0, b = 2, c = 4)
)
# `x` does not become named if RHS inputs are named
x <- c(1, 2, 3)
expect_identical(
replace_when(
x,
x == 1 ~ c(a = 0),
x == 3 ~ c(b = 4)
),
c(0, 2, 4)
)
})
test_that("replace_when() does not allow named `...`", {
# Purposefully stricter than `case_when()`
expect_snapshot(error = TRUE, {
replace_when(1, foo = TRUE ~ 2)
})
})
test_that("replace_when() compacts `NULL` inputs", {
expect_identical(
replace_when(1, NULL, TRUE ~ 2, NULL),
2
)
})
test_that("replace_when() is a no-op with zero conditions", {
# Unlike `case_when()`, where when zero conditions are supplied
# we don't know what kind of vector to build (and we refuse to
# build an `unspecified` vector, unlike `vec_case_when()`)
expect_identical(replace_when(1), 1)
expect_identical(replace_when(1, NULL), 1)
})
test_that("replace_when() works with data frames", {
x <- tibble(a = c(1, 2, 3, 1), b = c(2, 3, 4, 2))
expect_identical(
replace_when(
x,
vec_equal(x, tibble(a = 1, b = 2)) ~ NA
),
vec_assign(x, c(1, 4), NA)
)
expect_identical(
replace_when(
x,
vec_equal(x, tibble(a = 1, b = 2)) ~ tibble(a = 0, b = -1)
),
vec_assign(x, c(1, 4), tibble(a = 0, b = -1))
)
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.