Skip to content

Commit

Permalink
Implement map_vec()
Browse files Browse the repository at this point in the history
Fixes #435
  • Loading branch information
hadley committed Aug 27, 2022
1 parent a5dddf1 commit 9a82a56
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 0 deletions.
4 changes: 4 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ export(map_if)
export(map_int)
export(map_lgl)
export(map_raw)
export(map_vec)
export(modify)
export(modify2)
export(modify_at)
Expand Down Expand Up @@ -213,4 +214,7 @@ export(zap)
import(rlang)
import(vctrs)
importFrom(magrittr,"%>%")
importFrom(vctrs,vec_c)
importFrom(vctrs,vec_ptype_common)
importFrom(vctrs,vec_size)
useDynLib(purrr, .registration = TRUE)
24 changes: 24 additions & 0 deletions R/map.R
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
#' * `map_lgl()`, `map_int()`, `map_dbl()` and `map_chr()` return an
#' atomic vector of the indicated type (or die trying).
#'
#' * `map_vec()` simplifies to the common type of the output. It works with
#' most types of simple vectors like Date, POSIXct, factors, etc.
#'
#' * `map_dfr()` and `map_dfc()` return a data frame created by
#' row-binding and column-binding respectively. They require dplyr
#' to be installed. `map_df()` is an alias for `map_dfr()`.
Expand Down Expand Up @@ -218,6 +221,27 @@ map_raw <- function(.x, .f, ...) {
.Call(map_impl, environment(), ".x", ".f", "raw")
}


#' @rdname map
#' @param .ptype If `NULL`, the default, the output type is the common type
#' of the elements of the result. Otherwise, supply a "prototype" giving
#' the desired type of output.
#' @importFrom vctrs vec_c vec_size vec_ptype_common
#' @export
map_vec <- function(.x, .f, ..., .ptype = NULL) {
out <- map(.x, .f, ...)

.ptype <- vec_ptype_common(!!!out, .ptype = .ptype)
for (i in seq_along(out)) {
if (vec_size(out[[i]]) != 1L) {
stop_bad_element_vector(out[[i]], i, .ptype, 1L, what = "Result")
}
}

vec_c(!!!out, .ptype = .ptype)
}


#' @rdname map
#' @param .id Either a string or `NULL`. If a string, the output will contain
#' a variable with that name, storing either the name (if `.x` is named) or
Expand Down
9 changes: 9 additions & 0 deletions man/map.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions tests/testthat/_snaps/map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# requires output be length 1

Code
map_vec(1:2, ~ rep(1, .x))
Condition
Error in `stop_bad_type()`:
! Result 2 must be a single double, not a double vector of length 2

# requires common type of output

Code
map_vec(1:2, ~ if (.x == 1) factor("x") else 1)
Condition
Error in `map_vec()`:
! Can't combine `..1` <factor<bf275>> and `..2` <double>.

# can enforce .ptype

Code
map_vec(1:2, ~ factor("x"), .ptype = integer())
Condition
Error:
! Can't convert <factor<bf275>> to <integer>.

29 changes: 29 additions & 0 deletions tests/testthat/test-map.R
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,32 @@ test_that("map() with empty input copies names", {
expect_identical(map_chr(named_list, identity), named(chr()))
expect_identical(map_raw(named_list, identity), named(raw()))
})


# map_vec -----------------------------------------------------------------

test_that("still iterates using [[", {
df <- data.frame(x = 1, y = 2, z = 3)
expect_equal(map_vec(df, length), c(x = 1, y = 1, z = 1))
})

test_that("requires output be length 1", {
expect_snapshot(error = TRUE, {
map_vec(1:2, ~ rep(1, .x))
})
})

test_that("requires common type of output", {
out <- map_vec(1:2, ~ factor("x"))
expect_equal(out, factor(c("x", "x")))

expect_snapshot(error = TRUE, {
map_vec(1:2, ~ if (.x == 1) factor("x") else 1)
})
})

test_that("can enforce .ptype", {
expect_snapshot(error = TRUE, {
map_vec(1:2, ~ factor("x"), .ptype = integer())
})
})

0 comments on commit 9a82a56

Please sign in to comment.