diff --git a/NEWS.md b/NEWS.md index 2e286b62..ee2cd0b6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,8 @@ # purrr (development version) +* `list_transpose()` inspects all elements to determine the correct + template if it's not provided by the user (#1128, @krlmlr). + # purrr 1.0.2 * Fixed valgrind issue. diff --git a/R/list-transpose.R b/R/list-transpose.R index 136d60c3..cd9e737d 100644 --- a/R/list-transpose.R +++ b/R/list-transpose.R @@ -14,9 +14,9 @@ #' @param x A list of vectors to transpose. #' @param template A "template" that describes the output list. Can either be #' a character vector (where elements are extracted by name), or an integer -#' vector (where elements are extracted by position). Defaults to the names -#' of the first element of `x`, or if they're not present, the integer -#' indices. +#' vector (where elements are extracted by position). Defaults to the union +#' of the names of the elements of `x`, or if they're not present, the +#' union of the integer indices. #' @param simplify Should the result be [simplified][list_simplify]? #' * `TRUE`: simplify or die trying. #' * `NA`: simplify if possible. @@ -74,8 +74,19 @@ list_transpose <- function(x, if (length(x) == 0) { template <- integer() - } else { - template <- template %||% vec_index(x[[1]]) + } else if (is.null(template)) { + indexes <- map(x, vec_index) + call <- current_env() + withCallingHandlers( + template <- reduce(indexes, vec_set_union), + vctrs_error_ptype2 = function(e) { + cli::cli_abort( + "Can't combine named and unnamed vectors.", + arg = template, + call = call + ) + } + ) } if (!is.character(template) && !is.numeric(template)) { diff --git a/man/list_transpose.Rd b/man/list_transpose.Rd index 0dc8aabc..1e86d708 100644 --- a/man/list_transpose.Rd +++ b/man/list_transpose.Rd @@ -20,9 +20,9 @@ list_transpose( \item{template}{A "template" that describes the output list. Can either be a character vector (where elements are extracted by name), or an integer -vector (where elements are extracted by position). Defaults to the names -of the first element of \code{x}, or if they're not present, the integer -indices.} +vector (where elements are extracted by position). Defaults to the union +of the names of the elements of \code{x}, or if they're not present, the +union of the integer indices.} \item{simplify}{Should the result be \link[=list_simplify]{simplified}? \itemize{ diff --git a/tests/testthat/_snaps/list-transpose.md b/tests/testthat/_snaps/list-transpose.md index 7b5ab4a1..36a7dd4d 100644 --- a/tests/testthat/_snaps/list-transpose.md +++ b/tests/testthat/_snaps/list-transpose.md @@ -64,3 +64,11 @@ Error in `list_transpose()`: ! `template` must be a character or numeric vector, not a function. +# fail mixing named and unnamed vectors + + Code + test_list_transpose() + Condition + Error in `list_transpose()`: + ! Can't combine named and unnamed vectors. + diff --git a/tests/testthat/test-list-transpose.R b/tests/testthat/test-list-transpose.R index 272c8a49..91895abf 100644 --- a/tests/testthat/test-list-transpose.R +++ b/tests/testthat/test-list-transpose.R @@ -13,7 +13,7 @@ test_that("can use character template", { # Default: expect_equal( list_transpose(x, default = NA), - list(a = c(1, NA), b = c(2, 3)) + list(a = c(1, NA), b = c(2, 3), c = c(NA, 4)) ) # Change order @@ -130,3 +130,13 @@ test_that("validates inputs", { list_transpose(list(1), template = mean) }) }) + +test_that("fail mixing named and unnamed vectors", { + test_list_transpose <- function() { + x <- list(list(a = 1, b = 2), list(a = 3, b = 4)) + list_transpose(list(x = list(a = 1, b = 2), y = list(3, 4))) + } + expect_snapshot(error = TRUE, { + test_list_transpose() + }) +})