From 72a4dcf684318113030e0ba9873593285e6a8a83 Mon Sep 17 00:00:00 2001 From: cheryldietrich <52937302+cheryldietrich@users.noreply.github.com> Date: Thu, 15 Aug 2024 12:53:52 -0700 Subject: [PATCH 1/3] Changed magrittr pipes to base pipe in examples (#1139) Fixes #1133 --- NEWS.md | 6 +++--- R/deprec-when.R | 2 +- R/keep.R | 8 ++++---- README.Rmd | 2 +- README.md | 2 +- man/keep_at.Rd | 8 ++++---- man/when.Rd | 2 +- purrr.Rproj | 2 -- vignettes/base.Rmd | 8 ++++---- vignettes/other-langs.Rmd | 2 +- 10 files changed, 20 insertions(+), 22 deletions(-) diff --git a/NEWS.md b/NEWS.md index 636668d4..2e286b62 100644 --- a/NEWS.md +++ b/NEWS.md @@ -744,7 +744,7 @@ accessor(x[[1]])$foo to the equivalent pluck: ``` -x %>% pluck(1, accessor, "foo") +x |> pluck(1, accessor, "foo") ``` @@ -968,7 +968,7 @@ This is a compatibility release with dplyr 0.6.0. * `set_names()` is a snake-case alternative to `setNames()` with stricter equality checking, and more convenient defaults for pipes: - `x %>% set_names()` is equivalent to `setNames(x, x)` (#119). + `x |> set_names()` is equivalent to `setNames(x, x)` (#119). ## Row based functionals @@ -980,7 +980,7 @@ functions. * `map()` now always returns a list. Data frame support has been moved to `map_df()` and `dmap()`. The latter supports sliced data frames as a shortcut for the combination of `by_slice()` and `dmap()`: - `x %>% by_slice(dmap, fun, .collate = "rows")`. The conditional + `x |> by_slice(dmap, fun, .collate = "rows")`. The conditional variants `dmap_at()` and `dmap_if()` also support sliced data frames and will recycle scalar results to the slice size. diff --git a/R/deprec-when.R b/R/deprec-when.R index 677c3ca7..a1fb6f04 100644 --- a/R/deprec-when.R +++ b/R/deprec-when.R @@ -36,7 +36,7 @@ #' #' @keywords internal #' @examples -#' 1:10 %>% +#' 1:10 |> #' when( #' sum(.) <= 50 ~ sum(.), #' sum(.) <= 100 ~ sum(.)/2, diff --git a/R/keep.R b/R/keep.R index ab217f59..e1df34cd 100644 --- a/R/keep.R +++ b/R/keep.R @@ -68,12 +68,12 @@ compact <- function(.x, .p = identity) { #' @export #' @examples #' x <- c(a = 1, b = 2, cat = 10, dog = 15, elephant = 5, e = 10) -#' x %>% keep_at(letters) -#' x %>% discard_at(letters) +#' x |> keep_at(letters) +#' x |> discard_at(letters) #' #' # Can also use a function -#' x %>% keep_at(~ nchar(.x) == 3) -#' x %>% discard_at(~ nchar(.x) == 3) +#' x |> keep_at(~ nchar(.x) == 3) +#' x |> discard_at(~ nchar(.x) == 3) keep_at <- function(x, at) { where <- where_at(x, at, user_env = caller_env()) x[where] diff --git a/README.Rmd b/README.Rmd index 1f11d9c0..dc6bc5e5 100644 --- a/README.Rmd +++ b/README.Rmd @@ -52,7 +52,7 @@ library(purrr) mtcars |> split(mtcars$cyl) |> # from base R map(\(df) lm(mpg ~ wt, data = df)) |> - map(summary) %>% + map(summary) |> map_dbl("r.squared") ``` diff --git a/README.md b/README.md index 7809a9e8..ed4d3cc8 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ library(purrr) mtcars |> split(mtcars$cyl) |> # from base R map(\(df) lm(mpg ~ wt, data = df)) |> - map(summary) %>% + map(summary) |> map_dbl("r.squared") #> 4 6 8 #> 0.5086326 0.4645102 0.4229655 diff --git a/man/keep_at.Rd b/man/keep_at.Rd index feff8223..3f025a98 100644 --- a/man/keep_at.Rd +++ b/man/keep_at.Rd @@ -25,12 +25,12 @@ Keep/discard elements based on their name/position } \examples{ x <- c(a = 1, b = 2, cat = 10, dog = 15, elephant = 5, e = 10) -x \%>\% keep_at(letters) -x \%>\% discard_at(letters) +x |> keep_at(letters) +x |> discard_at(letters) # Can also use a function -x \%>\% keep_at(~ nchar(.x) == 3) -x \%>\% discard_at(~ nchar(.x) == 3) +x |> keep_at(~ nchar(.x) == 3) +x |> discard_at(~ nchar(.x) == 3) } \seealso{ \code{\link[=keep]{keep()}}/\code{\link[=discard]{discard()}} to keep/discard elements by value. diff --git a/man/when.Rd b/man/when.Rd index ff3c85a5..d7815456 100644 --- a/man/when.Rd +++ b/man/when.Rd @@ -37,7 +37,7 @@ valid match/condition is found the action is executed and the result of the action is returned. } \examples{ -1:10 \%>\% +1:10 |> when( sum(.) <= 50 ~ sum(.), sum(.) <= 100 ~ sum(.)/2, diff --git a/purrr.Rproj b/purrr.Rproj index 69b10ac6..cba1b6b7 100644 --- a/purrr.Rproj +++ b/purrr.Rproj @@ -19,5 +19,3 @@ BuildType: Package PackageUseDevtools: Yes PackageInstallArgs: --no-multiarch --with-keep.source PackageRoxygenize: rd,collate,namespace - -UseNativePipeOperator: Yes diff --git a/vignettes/base.Rmd b/vignettes/base.Rmd index 3b37d290..9f13787c 100644 --- a/vignettes/base.Rmd +++ b/vignettes/base.Rmd @@ -281,9 +281,9 @@ The pipe is particularly compelling when working with longer transformations. For example, the following code splits `mtcars` up by `cyl`, fits a linear model, extracts the coefficients, and extracts the first one (the intercept). ```{r, eval = modern_r} -mtcars %>% - split(mtcars$cyl) %>% - map(\(df) lm(mpg ~ wt, data = df)) %>% - map(coef) %>% +mtcars |> + split(mtcars$cyl) |> + map(\(df) lm(mpg ~ wt, data = df))|> + map(coef) |> map_dbl(1) ``` diff --git a/vignettes/other-langs.Rmd b/vignettes/other-langs.Rmd index dc1df51c..dd51b534 100644 --- a/vignettes/other-langs.Rmd +++ b/vignettes/other-langs.Rmd @@ -23,7 +23,7 @@ purrr draws inspiration from many related tools: However, the goal of purrr is not to try and simulate a purer functional programming language in R; we don't want to implement a second-class version of Haskell in R. The goal is to give you similar expressiveness to an FP language, while allowing you to write code that looks and works like R: -* Instead of point free (tacit) style, we use the pipe, `%>%`, to write code +* Instead of point free (tacit) style, we use the pipe, `|>`, to write code that can be read from left to right. * Instead of currying, we use `...` to pass in extra arguments. From 770bbfe95a062796d550847f10ff1c434ce1c7bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 15 Aug 2024 22:53:09 +0200 Subject: [PATCH 2/3] fix: `list_transpose()` takes into account all elements for the template (#1136) Closes #1128. --- NEWS.md | 3 +++ R/list-transpose.R | 21 ++++++++++++++++----- man/list_transpose.Rd | 6 +++--- tests/testthat/_snaps/list-transpose.md | 8 ++++++++ tests/testthat/test-list-transpose.R | 12 +++++++++++- 5 files changed, 41 insertions(+), 9 deletions(-) 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() + }) +}) From d022c4ebcce4160f5bdabd7ed3a09b97f3df734f Mon Sep 17 00:00:00 2001 From: "E. David Aja" Date: Thu, 15 Aug 2024 13:54:24 -0700 Subject: [PATCH 3/3] added imap_vec() (#1137) Fixes #1084 --- NAMESPACE | 1 + NEWS.md | 1 + R/imap.R | 7 +++++++ man/imap.Rd | 3 +++ tests/testthat/test-imap.R | 1 + 5 files changed, 13 insertions(+) diff --git a/NAMESPACE b/NAMESPACE index e754bc4a..bb906a65 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -60,6 +60,7 @@ export(imap_dfr) export(imap_int) export(imap_lgl) export(imap_raw) +export(imap_vec) export(imodify) export(insistently) export(invoke) diff --git a/NEWS.md b/NEWS.md index ee2cd0b6..b26c4487 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ # purrr (development version) +* Added `imap_vec()` (#1084) * `list_transpose()` inspects all elements to determine the correct template if it's not provided by the user (#1128, @krlmlr). diff --git a/R/imap.R b/R/imap.R index fa1d9e52..ec1b01ff 100644 --- a/R/imap.R +++ b/R/imap.R @@ -56,6 +56,13 @@ imap_dbl <- function(.x, .f, ...) { map2_dbl(.x, vec_index(.x), .f, ...) } +#' @rdname imap +#' @export +imap_vec <- function(.x, .f, ...) { + .f <- as_mapper(.f, ...) + map2_vec(.x, vec_index(.x), .f, ...) +} + #' @export #' @rdname imap diff --git a/man/imap.Rd b/man/imap.Rd index ae081b01..b453d245 100644 --- a/man/imap.Rd +++ b/man/imap.Rd @@ -6,6 +6,7 @@ \alias{imap_chr} \alias{imap_int} \alias{imap_dbl} +\alias{imap_vec} \alias{iwalk} \title{Apply a function to each element of a vector, and its index} \usage{ @@ -19,6 +20,8 @@ imap_int(.x, .f, ...) imap_dbl(.x, .f, ...) +imap_vec(.x, .f, ...) + iwalk(.x, .f, ...) } \arguments{ diff --git a/tests/testthat/test-imap.R b/tests/testthat/test-imap.R index 79314d7e..bd1536e1 100644 --- a/tests/testthat/test-imap.R +++ b/tests/testthat/test-imap.R @@ -13,6 +13,7 @@ test_that("atomic vector imap works", { expect_length(imap_chr(x, paste), 3) expect_equal(imap_int(x, ~ .x + as.integer(.y)), x * 2) expect_equal(imap_dbl(x, ~ .x + as.numeric(.y)), x * 2) + expect_equal(imap_vec(x, ~ .x + as.numeric(.y)), x * 2) }) test_that("iwalk returns invisibly", {