test_that("number of rows is preserved", { df <- tibble( x = 1:3, y = list(NULL, c(a = 1), c(a = 1, b = 2)) ) out <- df %>% unnest_wider(y) expect_equal(nrow(out), 3) }) test_that("simplifies length-1 lists", { df <- tibble( x = 1:2, y = list( list(a = 1, b = 2, c = c(1, 2)), list(a = 3) ) ) out <- df %>% unnest_wider(y) expect_equal(out$a, c(1, 3)) expect_equal(out$b, c(2, NA)) expect_equal(out$c, list(c(1, 2), NULL)) # Works when casting too out <- df %>% unnest_wider(y, ptype = list(a = integer(), b = integer()) ) expect_equal(out$a, c(1L, 3L)) expect_equal(out$b, c(2L, NA)) expect_equal(out$c, list(c(1, 2), NULL)) }) test_that("treats data frames like lists where we have type info about each element", { df <- tibble(x = 1:2, y = list(tibble(a = 1:2))) out <- df %>% unnest_wider(y) expect_named(out, c("x", "a")) expect_equal(nrow(out), 2) # We know the types of this, even though we can't simplify it expect_identical(attr(out$a, "ptype"), integer()) df <- tibble(x = 1:2, y = list(list(a = 1:2))) out <- df %>% unnest_wider(y) expect_named(out, c("x", "a")) expect_equal(nrow(out), 2) # We don't know the types of this expect_identical(class(out$a), "list") }) test_that("unnest_wider - bad inputs generate errors", { df <- tibble(x = 1, y = list(mean)) expect_snapshot((expect_error( unnest_wider(df, y) ))) }) test_that("list of 0-length vectors yields no new columns", { df <- tibble(x = 1:2, y = list(integer(), integer())) expect_named(unnest_wider(df, y), "x") # similarly when empty df <- tibble(x = integer(), y = list()) expect_named(unnest_wider(df, y), "x") # similarly when using list_of() with 0-length elements df <- tibble(x = 1:2, y = list_of(integer(), integer())) expect_named(unnest_wider(df, y), "x") }) test_that("list-col with only `NULL` works (#1186)", { df <- tibble(x = list(NULL)) expect_identical(unnest_wider(df, x), tibble::new_tibble(list(), nrow = 1L)) }) test_that("empty list yields no new columns", { df <- tibble(x = list()) expect_identical(unnest_wider(df, x), tibble()) }) test_that("list_of columns can be unnested", { df <- tibble(x = 1:2, y = list_of(c(a = 1L), c(a = 1L, b = 2L))) expect_named(unnest_wider(df, y), c("x", "a", "b")) df <- tibble(x = 1:2, y = list_of(c(a = 1L), c(b = 1:2))) expect_named(unnest_wider(df, y), c("x", "a", "b1", "b2")) }) test_that("names_sep creates unique names", { df <- tibble( x = list("a", c("a", "b", "c")), y = list(c(a = 1), c(b = 2, a = 1)) ) expect_warning(out <- unnest_wider(df, x, names_sep = "_"), NA) expect_named(out, c("x_1", "x_2", "x_3", "y")) expect_warning(out <- unnest_wider(df, y, names_sep = "_"), NA) expect_named(out, c("x", "y_a", "y_b")) expect_equal(out$y_a, c(1, 1)) }) test_that("`names_sep` works with empty elements (#1185)", { df <- tibble(x = list(c(a = 1L), c(a = integer()))) out <- unnest_wider(df, x, names_sep = "_") expect_identical(out, tibble(x_a = c(1L, NA))) }) test_that("`names_sep` works with data frame columns", { df <- tibble(x = tibble(a = 1, b = 2)) out <- unnest_wider(df, x, names_sep = "_") expect_named(out, c("x_a", "x_b")) }) test_that("`names_sep` works with named non-list atomic vectors", { # Equivalent to `df <- tibble(x = list_of(c(a = 1), c(b = 2)))` df <- tibble(x = c(a = 1, b = 2)) out <- unnest_wider(df, x, names_sep = "_") expect_named(out, c("x_a", "x_b")) }) test_that("df-cols can be unnested (#1188)", { df <- tibble(a = 1:3, b = tibble(x = 1:3, y = 1:3)) out <- unnest_wider(df, b) expect_identical(out, unpack(df, b)) }) test_that("df-cols result in list-ofs when `simplify = FALSE`", { df <- tibble(a = 1:3, b = tibble(x = 1:3, y = 1:3)) out <- unnest_wider(df, b, simplify = FALSE) expect_identical(out, tibble(a = 1:3, x = list_of(1L, 2L, 3L), y = list_of(1L, 2L, 3L))) }) test_that("unnesting mixed empty types retains the column (#1125)", { df <- tibble(col = list(list(a = list()), list(a = integer()))) expect_identical(unnest_wider(df, col), tibble(a = c(NA, NA))) }) test_that("can unnest mixed empty types with `strict = FALSE`", { df <- tibble(col = list( list(a = "x"), list(a = list()), list(a = integer()) )) expect_identical( unnest_wider(df, col)$a, c("x", NA, NA) ) # They are replaced with `NULL` before simplification expect_identical( unnest_wider(df, col, simplify = FALSE)$a, list("x", NULL, NULL) ) }) test_that("can't unnest mixed empty types when in strict mode", { df <- tibble(col = list(list(a = list()), list(a = 1L))) # Not strict, useful for JSON data but doesn't follow vctrs type rules expect_identical(unnest_wider(df, col), tibble(a = c(NA, 1L))) # Strict means they can't be combined expect_identical( unnest_wider(df, col, strict = TRUE), tibble(a = list(list(), 1L)) ) }) test_that("can unnest multiple columns wider at once (#740)", { df <- tibble( x = list(list(a = 1), list(a = 2)), y = list(list(b = 1, c = "x"), list(b = 2, c = "y")) ) expect_identical( unnest_wider(df, c(x, y)), tibble(a = c(1, 2), b = c(1, 2), c = c("x", "y")) ) }) test_that("can unnest a vector with a mix of named/unnamed elements (#1200 comment, #1367)", { df <- tibble(x = c(a = 1L, 2L)) out <- unnest_wider(df, x, names_sep = "_") expect_identical(out$x_a, c(1L, NA)) expect_identical(out$x_1, c(NA, 2L)) }) test_that("can unnest a list with a mix of named/unnamed elements (#1200 comment)", { df <- tibble(x = list(a = 1:2, 3:4)) out <- unnest_wider(df, x, names_sep = "_") expect_identical(out$x_1, c(1L, 3L)) expect_identical(out$x_2, c(2L, 4L)) }) test_that("integer names are generated before applying `names_sep` (#1200 comment, #1367)", { df <- tibble(col = list(set_names(1, ""))) out <- unnest_wider(df, col, names_sep = "_") expect_named(out, "col_1") df <- tibble(col = list(set_names(1:2, c("", "")))) out <- unnest_wider(df, col, names_sep = "_") expect_named(out, c("col_1", "col_2")) }) test_that("integer names are generated for partially named vectors (#1367)", { df <- tibble(col = list(set_names(1:4, c("x", "", "z", "")))) out <- unnest_wider(df, col, names_sep = "_") expect_named(out, c("col_x", "col_2", "col_z", "col_4")) df <- tibble(col = list( set_names(1:4, c("x", "", "z", "")), set_names(5:8, c("", "", "z", "")) )) out <- unnest_wider(df, col, names_sep = "_") expect_named(out, c("col_x", "col_2", "col_z", "col_4", "col_1")) expect_identical(out$col_x, c(1L, NA)) expect_identical(out$col_1, c(NA, 5L)) }) test_that("`NA_character_` name is treated like the empty string (#1200 comment)", { col <- list( set_names(1, "a"), set_names(1, NA_character_), set_names(1, "") ) df <- tibble(col = col) out <- unnest_wider(df, col, names_sep = "_") expect_identical(out$col_a, c(1, NA, NA)) expect_identical(out$col_1, c(NA, 1, 1)) }) test_that("can combine ` + >`", { df <- tibble(col = list(list(a = 1:2), list_of(a = 1L))) out <- unnest_wider(df, col) expect_identical(out$a, list(1:2, 1L)) }) test_that("can't unnest unnamed elements without `names_sep` (#1367)", { df <- tibble(col = list(1)) expect_snapshot(error = TRUE, { unnest_wider(df, col) }) df <- tibble(col = list(set_names(1, ""))) expect_snapshot(error = TRUE, { unnest_wider(df, col) }) df <- tibble(col = list(set_names(1, NA_character_))) expect_snapshot(error = TRUE, { unnest_wider(df, col) }) # Partially missing within an element df <- tibble(col = list(c(a = 1), c(a = 1, 2))) expect_snapshot(error = TRUE, { unnest_wider(df, col) }) }) test_that("catches duplicate inner names in the same vector", { df <- tibble(col = list(c(a = 1, a = 2))) expect_snapshot(error = TRUE, { unnest_wider(df, col) }) expect_snapshot({ out <- unnest_wider(df, col, names_repair = "unique") }) expect_named(out, c("a...1", "a...2")) }) test_that("unnest_wider() advises on outer / inner name duplication (#1367)", { df <- tibble(x = 1, y = list(list(x = 2))) expect_snapshot(error = TRUE, { unnest_wider(df, y) }) }) test_that("unnest_wider() advises on inner / inner name duplication (#1367)", { df <- tibble(x = 1, y = list(list(a = 2)), z = list(list(a = 3))) expect_snapshot(error = TRUE, { unnest_wider(df, c(y, z)) }) }) test_that("unnest_wider() works with foreign lists recognized by `vec_is_list()` (#1327)", { new_foo <- function(...) { structure(list(...), class = c("foo", "list")) } # With empty types df <- tibble(x = new_foo(new_foo(a = 1, b = integer()))) expect_identical(unnest_wider(df, x, strict = TRUE), tibble(a = 1, b = NA_integer_)) # With `NULL`s df <- tibble(x = new_foo(new_foo(a = 1, b = NULL))) expect_identical(unnest_wider(df, x), tibble(a = 1, b = NA)) }) test_that("unnest_wider() validates its inputs", { df <- tibble(x = list(a = 1:2, b = 3:4)) expect_snapshot(error = TRUE, { unnest_wider(1) unnest_wider(df) unnest_wider(df, x, names_sep = 1) unnest_wider(df, x, strict = 1) }) }) test_that("invariant - final number of columns depends on element sizes", { df <- tibble(x = list_of(.ptype = integer())) expect_identical(dim(unnest_wider(df, x)), c(0L, 0L)) df <- tibble(x = list_of(NULL, .ptype = integer())) expect_identical(dim(unnest_wider(df, x)), c(1L, 0L)) df <- tibble(x = list_of(integer())) expect_identical(dim(unnest_wider(df, x)), c(1L, 0L)) df <- tibble(x = list_of(c(a = 1L))) expect_identical(dim(unnest_wider(df, x)), c(1L, 1L)) df <- tibble(x = list_of(c(a = 1L), c(a = 1L, b = 2L, c = 3L))) expect_identical(dim(unnest_wider(df, x)), c(2L, 3L)) df <- tibble(x = list_of(c(a = 1L, c = 3L), c(a = 1L, b = 2L))) expect_identical(dim(unnest_wider(df, x)), c(2L, 3L)) }) test_that("invariant - for list_of special case, final number of columns and types comes from ptype columns (#1187)", { df <- tibble(x = list_of(.ptype = tibble(a = integer()))) expect_identical(unnest_wider(df, x), tibble(a = integer())) df <- tibble(x = list_of(NULL, .ptype = tibble(a = integer()))) expect_identical(unnest_wider(df, x), tibble(a = NA_integer_)) df <- tibble(x = list_of(tibble(a = integer()))) expect_identical(unnest_wider(df, x), tibble(a = NA_integer_)) df <- tibble(x = list_of(tibble(a = 1L))) expect_identical(unnest_wider(df, x), tibble(a = 1L)) df <- tibble(x = list_of(tibble(a = 1:2))) expect_identical(unnest_wider(df, x), tibble(a = list_of(1:2))) })