test_that("from_as_list_of_vectors = TRUE / to_as_list_of_vectors = TRUE", { expect_identical( vec_recode_values( x = c(1, 2, 3), from = list(c(2, 3), c(4, 1)), to = list( c("a", "b", "c"), c("d", "e", "f") ), from_as_list_of_vectors = TRUE, to_as_list_of_vectors = TRUE ), c("d", "b", "c") ) }) test_that("from_as_list_of_vectors = TRUE / to_as_list_of_vectors = TRUE (optimized to `from_as_list_of_vectors = FALSE`, `to_as_list_of_vectors = FALSE`)", { # First optimized to `to_as_list_of_vectors = FALSE` # Then further optimized to `from_as_list_of_vectors = FALSE` expect_identical( vec_recode_values( x = c(1, 2, 3), from = list(c(2, 3), c(4, 1)), to = list( "a", "b" ), from_as_list_of_vectors = TRUE, to_as_list_of_vectors = TRUE ), c("b", "a", "a") ) }) test_that("from_as_list_of_vectors = TRUE / to_as_list_of_vectors = FALSE (always optimized to `from_as_list_of_vectors = FALSE`)", { expect_identical( vec_recode_values( x = c(1, 2, 3), from = list(c(2, 3), c(4, 1)), to = c("a", "b"), from_as_list_of_vectors = TRUE, to_as_list_of_vectors = FALSE ), c("b", "a", "a") ) }) test_that("from_as_list_of_vectors = FALSE / to_as_list_of_vectors = TRUE", { expect_identical( vec_recode_values( x = c(1, 2, 3), from = c(2, 1), to = list( c("a", "b", "c"), c("d", "e", "f") ), from_as_list_of_vectors = FALSE, to_as_list_of_vectors = TRUE ), c("d", "b", NA) ) }) test_that("from_as_list_of_vectors = FALSE / to_as_list_of_vectors = TRUE (optimized to `to_as_list_of_vectors = FALSE`)", { expect_identical( vec_recode_values( x = c(1, 2, 3), from = c(2, 1), to = list( "a", "b" ), from_as_list_of_vectors = FALSE, to_as_list_of_vectors = TRUE ), c("b", "a", NA) ) }) test_that("from_as_list_of_vectors = FALSE / to_as_list_of_vectors = FALSE", { expect_identical( vec_recode_values( x = c(1, 2, 3), from = c(2, 1), to = c("a", "b"), from_as_list_of_vectors = FALSE, to_as_list_of_vectors = FALSE ), c("b", "a", NA) ) }) test_that("can treat list input as a vector (i.e. not as a container of vectors)", { # This is why we have `from_as_list_of_vectors` and `to_as_list_of_vectors` as arguments expect_identical( vec_recode_values( x = list(1, 2, 3), from = list(2, 5, 1), to = list("x", 1:2, 1:3), ), list(1:3, "x", NULL) ) }) test_that("`to` names are kept during `from_as_list_of_vectors, !to_as_list_of_vectors` optimization recycling", { expect_identical( vec_recode_values( x = c(2, 1, 4, 5), from = list(1:2, 3:4, 5:6), to = c(a = "x", b = "y", c = "z"), from_as_list_of_vectors = TRUE ), c(a = "x", a = "x", b = "y", c = "z") ) }) test_that("`to` list names are dropped with `to_as_list_of_vectors`", { # With `to_as_list_of_vectors` optimization expect_identical( vec_recode_values( x = c(1, 1), from = 1, to = list(a = "x"), to_as_list_of_vectors = TRUE ), c("x", "x") ) # Without `to_as_list_of_vectors` optimization expect_identical( vec_recode_values( x = c(1, 1), from = 1, to = list(a = c("x", "y")), to_as_list_of_vectors = TRUE ), c("x", "y") ) }) test_that("`to` is recycled to `from_size` of `to_as_list_of_vectors`", { # With `to_as_list_of_vectors` expect_identical( vec_recode_values( x = c(1, 2, 3, 1, 4), from = c(1, 2), to = list(2), to_as_list_of_vectors = TRUE ), c(2, 2, NA, 2, NA) ) expect_identical( vec_recode_values( x = c(1, 2, 3, 1, 4), from = c(1, 2), to = list(c(2, 3, 4, 5, 6)), to_as_list_of_vectors = TRUE ), c(2, 3, NA, 5, NA) ) # This doesn't make too much sense for a user to provide, but is consistent expect_identical( vec_recode_values( x = c(1, 2, 3, 1, 4), from = list(c(1, 2), 4), to = list(2), from_as_list_of_vectors = TRUE, to_as_list_of_vectors = TRUE ), c(2, 2, NA, 2, 2) ) # This doesn't make too much sense for a user to provide, but is consistent expect_identical( vec_recode_values( x = c(1, 2, 3, 1, 4), from = list(c(1, 2), 4), to = list(c(2, 3, 4, 5, 6)), from_as_list_of_vectors = TRUE, to_as_list_of_vectors = TRUE ), c(2, 3, NA, 5, 6) ) # Without `to_as_list_of_vectors` expect_identical( vec_recode_values( x = c(1, 2, 3, 1), from = c(1, 2), to = 2 ), c(2, 2, NA, 2) ) expect_identical( vec_recode_values( x = c(1, 2, 3, 1, 4), from = list(c(1, 2), 4), to = 2, from_as_list_of_vectors = TRUE ), c(2, 2, NA, 2, 2) ) }) test_that("`from` list names are dropped with `from_as_list_of_vectors`", { # With `from_as_list_of_vectors, !to_as_list_of_vectors` optimization expect_identical( vec_recode_values( x = c(1, 2), from = list(a = 1, b = c(2, 3, 4)), to = c("x", "y"), from_as_list_of_vectors = TRUE ), c("x", "y") ) # Without `from_as_list_of_vectors, !to_as_list_of_vectors` optimization expect_identical( vec_recode_values( x = c(1, 2), from = list(a = 1, b = c(2, 3, 4)), to = list(x = c("x1", "x2"), y = c("y1", "y2")), from_as_list_of_vectors = TRUE, to_as_list_of_vectors = TRUE ), c("x1", "y2") ) }) test_that("`vec_replace_values()` retains names of `x`", { # Mimicking `[<-` and `base::replace()`. # Note how `vec_recode_values()` "creates a new vector", # so it pulls the names from `to` and `default`. expect_identical( vec_replace_values( x = c(a = 1, b = 2, c = 0), from = c(2, 1), to = c(x = 3, y = 4) ), c(a = 4, b = 3, c = 0) ) expect_identical( vec_recode_values( x = c(a = 1, b = 2, c = 0), from = c(2, 1), to = c(x = 3, y = 4) ), c(y = 4, x = 3, NA) ) }) test_that("`unmatched` errors are correct", { expect_snapshot(error = TRUE, { vec_recode_values(c(1, 2), from = 1, to = 0, unmatched = "error") }) expect_snapshot(error = TRUE, { # `NA` must be matched! vec_recode_values(c(1, NA), from = 1, to = 0, unmatched = "error") }) expect_snapshot(error = TRUE, { # Many locations vec_recode_values(1:100, from = 1, to = 0, unmatched = "error") }) }) test_that("`x` and `from` common type errors are correct", { expect_snapshot(error = TRUE, { vec_recode_values(1, from = "a", to = 1) }) expect_snapshot(error = TRUE, { vec_recode_values( 1, from = list("a"), to = 1, from_as_list_of_vectors = TRUE ) }) }) test_that("`to` and `default` `ptype` errors are correct when it is inferred", { expect_snapshot(error = TRUE, { vec_recode_values( 1, from = 1:2, to = list(1, "x"), to_as_list_of_vectors = TRUE ) }) expect_snapshot(error = TRUE, { vec_recode_values( 1, from = 1:2, to = list(1, 2), default = "x", to_as_list_of_vectors = TRUE ) }) expect_snapshot(error = TRUE, { vec_recode_values(1, from = 1:2, to = 1, default = "x") }) }) test_that("`to` and `default` `ptype` errors are correct when it is user supplied", { expect_snapshot(error = TRUE, { vec_recode_values(1, from = 1, to = 1, ptype = foobar()) }) expect_snapshot(error = TRUE, { vec_recode_values(1, from = 1, to = 1, ptype = character()) }) expect_snapshot(error = TRUE, { vec_recode_values( 1, from = 1, to = list(a = 1), ptype = character(), to_as_list_of_vectors = TRUE ) }) expect_snapshot(error = TRUE, { vec_recode_values(1, from = 1, to = "x", default = 1, ptype = character()) }) }) test_that("`to` size is validated", { expect_snapshot(error = TRUE, { vec_recode_values(1:5, from = 1, to = 2:3) }) expect_snapshot(error = TRUE, { vec_recode_values( 1:5, from = list(1), to = 2:3, from_as_list_of_vectors = TRUE ) }) expect_snapshot(error = TRUE, { vec_recode_values( 1:5, from = 1, to = list(2, 3), to_as_list_of_vectors = TRUE ) }) expect_snapshot(error = TRUE, { vec_recode_values( 1:5, from = list(1), to = list(2, 3), from_as_list_of_vectors = TRUE, to_as_list_of_vectors = TRUE ) }) expect_snapshot(error = TRUE, { vec_recode_values( 1:5, from = 1, to = list(a = 2:3), to_as_list_of_vectors = TRUE ) }) }) test_that("`default` size is validated", { expect_snapshot(error = TRUE, { vec_recode_values(1:5, from = 1, to = 2, default = 1:2) }) }) test_that("`x` must be a vector", { expect_snapshot(error = TRUE, { vec_recode_values(foobar(), from = 1, to = 2, x_arg = ".x") }) }) test_that("`from` must be a vector or list of vectors", { expect_snapshot(error = TRUE, { vec_recode_values(1, from = foobar(), to = 2, from_arg = ".from") }) expect_snapshot(error = TRUE, { vec_recode_values( 1, from = 1, to = 2, from_as_list_of_vectors = TRUE, from_arg = ".from" ) }) expect_snapshot(error = TRUE, { vec_recode_values( 1, from = list(a = foobar()), to = 2, from_as_list_of_vectors = TRUE, from_arg = ".from" ) }) }) test_that("`to` must be a vector or list of vectors", { expect_snapshot(error = TRUE, { vec_recode_values(1, from = 1, to = foobar(), to_arg = ".to") }) expect_snapshot(error = TRUE, { vec_recode_values( 1, from = 1, to = 2, to_as_list_of_vectors = TRUE, to_arg = ".to" ) }) expect_snapshot(error = TRUE, { vec_recode_values( 1, from = 1, to = list(a = foobar()), to_as_list_of_vectors = TRUE, to_arg = ".to" ) }) }) test_that("`default` must be a vector", { expect_snapshot(error = TRUE, { vec_recode_values( 1, from = 1, to = 2, default = foobar(), default_arg = ".default" ) }) }) test_that("`from_as_list_of_vectors` and `to_as_list_of_vectors` are validated", { expect_snapshot(error = TRUE, { vec_recode_values(1, from = 1, to = 1, from_as_list_of_vectors = "x") }) expect_snapshot(error = TRUE, { vec_recode_values(1, from = 1, to = 1, to_as_list_of_vectors = "x") }) expect_snapshot(error = TRUE, { vec_replace_values(1, from = 1, to = 1, from_as_list_of_vectors = "x") }) expect_snapshot(error = TRUE, { vec_replace_values(1, from = 1, to = 1, to_as_list_of_vectors = "x") }) }) test_that("`unmatched` is validated", { expect_snapshot(error = TRUE, { vec_recode_values(1, from = 1, to = 1, unmatched = "e") }) }) test_that("proof that `ptype` finalization is important", { # Imagine you have an input logical vector you are remapping # and it happens to only have `NA`s x <- c(NA, NA) from <- NA to <- FALSE # If no `ptype` finalization happened, then `ptype = x` would result in # `unspecified` being the output type and these would error expect_identical( vec_recode_values(x, from = from, to = to, default = x, ptype = x), c(FALSE, FALSE) ) expect_identical( vec_replace_values(x, from = from, to = to), c(FALSE, FALSE) ) }) test_that("common `ptype` of `to` isn't finalized until `default` has been included", { # If the common type of `to` is finalized early, we get `logical`, which can't # combine with `default`'s `character` type expect_identical( vec_recode_values( x = "a", from = "a", to = list(NA), default = "x", to_as_list_of_vectors = TRUE ), NA_character_ ) expect_identical( vec_recode_values( x = "a", from = "b", to = list(NA), default = "x", to_as_list_of_vectors = TRUE ), "x" ) }) test_that("extraneous `to` attributes don't end up on the final output", { x <- c(1, 2, 3) # TODO: Ideally the attributes wouldn't show up on the output, but # `list_combine()` doesn't clear them because `vec_ptype(to)` retains # them for some reason # https://github.com/r-lib/vctrs/issues/2025 from <- c(2, 3) to <- structure(c(0, -1), foo = "bar") expect_identical( vec_recode_values(x, from = from, to = to), structure(c(NA, 0, -1), foo = "bar") ) # TODO: Ideally the attributes wouldn't show up on the output, but # `list_combine()` doesn't clear them because `vec_ptype(to)` retains # them for some reason # https://github.com/r-lib/vctrs/issues/2025 from <- 2 to <- list( structure(c(0, -1, -2), foo = "bar") ) expect_identical( vec_recode_values(x, from = from, to = to, to_as_list_of_vectors = TRUE), structure(c(NA, -1, NA), foo = "bar") ) # Note that as soon as you force a `ptype2` computation, the attributes # disappear anyways, suggesting an inconsistency # `ptype2` forced by `default` from <- c(2, 3) to <- structure(c(0, -1), foo = "bar") expect_identical( vec_recode_values(x, from = from, to = to, default = NA_real_), c(NA, 0, -1) ) # `ptype2` forced by multiple `to` values from <- c(2, 3) to <- list( structure(c(0, -1, -2), foo = "bar"), c(-3, -4, -5) ) expect_identical( vec_recode_values(x, from = from, to = to, to_as_list_of_vectors = TRUE), c(NA, -1, -5) ) }) test_that("extraneous `x` attributes don't end up on the final output", { # TODO: Ideally the attributes wouldn't show up on the output, but # `list_combine()` doesn't clear them because `vec_ptype(x)` retains # them for some reason # https://github.com/r-lib/vctrs/issues/2025 x <- structure(1, foo = "bar") expect_identical( vec_replace_values(x, from = 1, to = 2), structure(2, foo = "bar") # 2 ) }) test_that("first `from` wins when there are overlaps", { # Same as `vec_case_when()` expect_identical( vec_recode_values( x = 1, from = c(1, 1), to = c("a", "b") ), "a" ) expect_identical( vec_recode_values( x = 1, from = list(c(1, 3), c(1, 2)), to = c("a", "b"), from_as_list_of_vectors = TRUE ), "a" ) expect_identical( vec_recode_values( x = c(1, 2), from = c(1, 1), to = list(c("a", "b"), c("c", "d")), to_as_list_of_vectors = TRUE ), c("a", NA) ) expect_identical( vec_recode_values( x = c(1, 2), from = list(c(1, 3), c(1, 2)), to = list(c("a", "b"), c("c", "d")), from_as_list_of_vectors = TRUE, to_as_list_of_vectors = TRUE ), c("a", "d") ) }) test_that("works when `from` is a list of size 1 elements and `to` doesn't simplify", { # This is a case where we don't actually build the `from_map` because the # `from` size doesn't change as it flattens expect_identical( vec_recode_values( x = c(1, 2), from = list(1, 2), to = list(c("a", "b"), c("c", "d")), from_as_list_of_vectors = TRUE, to_as_list_of_vectors = TRUE ), c("a", "d") ) }) test_that("works when `to` is a length >1 vector and every element of `x` is matched by `from`", { # This is an optimized case where we directly use the index provided by # `vec_match()` to slice `to` with expect_identical( vec_recode_values( x = c(1, 2), from = c(2, 1), to = c("a", "b") ), c("b", "a") ) }) test_that("data frames - vector `from`, vector `to`", { x <- data_frame(a = 1:3, b = 3:5) from <- data_frame(a = int(3, 1), b = c(5, 3)) # Recycling to <- data_frame(c = "a", d = "y") expect_identical( vec_recode_values(x, from = from, to = to), data_frame(c = c("a", NA, "a"), d = c("y", NA, "y")) ) to <- data_frame(c = c("a", "b"), d = c("x", "y")) expect_identical( vec_recode_values(x, from = from, to = to), data_frame(c = c("b", NA, "a"), d = c("y", NA, "x")) ) # List `from`, vector `to` # List `from`, list `to` }) test_that("data frames - vector `from`, list `to`", { x <- data_frame(a = 1:3, b = 3:5) from <- data_frame(a = int(3, 1), b = c(5, 3)) # Recycling to `from` size and `x` size to <- list( data_frame(c = "a", d = "y") ) expect_identical( vec_recode_values(x, from = from, to = to, to_as_list_of_vectors = TRUE), data_frame(c = c("a", NA, "a"), d = c("y", NA, "y")) ) # Recycling to `from` size to <- list( data_frame(c = c("a", "b", "c"), d = c("x", "y", "z")) ) expect_identical( vec_recode_values(x, from = from, to = to, to_as_list_of_vectors = TRUE), data_frame(c = c("a", NA, "c"), d = c("x", NA, "z")) ) # Recycling to `x` size to <- list( data_frame(c = "a", d = "x"), data_frame(c = "b", d = "y") ) expect_identical( vec_recode_values(x, from = from, to = to, to_as_list_of_vectors = TRUE), data_frame(c = c("b", NA, "a"), d = c("y", NA, "x")) ) }) test_that("data frames - list `from`, vector `to`", { x <- data_frame(a = 1:3, b = 3:5) # Recycling `to` to `from` size (this would be strange) from <- list( data_frame(a = 1L, b = 3L), data_frame(a = 3:4, b = 5:6) ) to <- data_frame(c = "a", d = "x") expect_identical( vec_recode_values(x, from = from, to = to, from_as_list_of_vectors = TRUE), data_frame(c = c("a", NA, "a"), d = c("x", NA, "x")) ) from <- list( data_frame(a = 1L, b = 3L), data_frame(a = 3:4, b = 5:6) ) to <- data_frame(c = c("a", "b"), d = c("x", "y")) expect_identical( vec_recode_values(x, from = from, to = to, from_as_list_of_vectors = TRUE), data_frame(c = c("a", NA, "b"), d = c("x", NA, "y")) ) }) test_that("data frames - list `from`, list `to`", { x <- data_frame(a = 1:3, b = 3:5) # Recycling `to` to `from` size (this would be strange) # Recycling `to` elements to `x` size from <- list( data_frame(a = 1L, b = 3L), data_frame(a = 3:4, b = 5:6) ) to <- list( data_frame(c = "a", d = "x") ) expect_identical( vec_recode_values( x, from = from, to = to, from_as_list_of_vectors = TRUE, to_as_list_of_vectors = TRUE ), data_frame(c = c("a", NA, "a"), d = c("x", NA, "x")) ) # Recycling `to` elements to `x` size from <- list( data_frame(a = 1L, b = 3L), data_frame(a = 3:4, b = 5:6) ) to <- list( data_frame(c = "a", d = "x"), data_frame(c = "b", d = "y") ) expect_identical( vec_recode_values( x, from = from, to = to, from_as_list_of_vectors = TRUE, to_as_list_of_vectors = TRUE ), data_frame(c = c("a", NA, "b"), d = c("x", NA, "y")) ) from <- list( data_frame(a = 1L, b = 3L), data_frame(a = 3:4, b = 5:6) ) to <- list( data_frame(c = c("a", "b", "c"), d = c("x", "y", "z")), data_frame(c = c("aa", "bb", "cc"), d = c("xx", "yy", "zz")) ) expect_identical( vec_recode_values( x, from = from, to = to, from_as_list_of_vectors = TRUE, to_as_list_of_vectors = TRUE ), data_frame(c = c("a", NA, "cc"), d = c("x", NA, "zz")) ) })