-
Notifications
You must be signed in to change notification settings - Fork 274
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Apply vctrs principles to map()
and modify()
#894
Changes from 20 commits
9a82a56
194828f
a717b6c
7733dc1
bdde69f
63119fd
0aff22d
b852864
5671d9d
4d5b102
ed04346
e093621
5167acc
08fe6e6
b2c09b3
903bdd3
e203d7e
3da5be6
8f82675
4983571
961eeea
2e7af8c
0b7ce2e
ecae803
524e484
9d45521
87cd328
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -93,41 +93,18 @@ modify <- function(.x, .f, ...) { | |
modify.default <- function(.x, .f, ...) { | ||
.f <- as_mapper(.f, ...) | ||
|
||
for (i in seq_along(.x)) { | ||
list_slice2(.x, i) <- .f(.x[[i]], ...) | ||
if (vec_is_list(.x) || is.data.frame(.x)) { | ||
out <- map(vec_proxy(.x), .f, ...) | ||
vec_restore(out, .x) | ||
hadley marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} else if (vec_is(.x)) { | ||
map_vec(.x, .f, ..., .ptype = .x) | ||
} else if (is.null(.x) || is.list(.x)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a bit strange to go through that path for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had this originally, but it also felt weird — it is nice that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW I also prefer the explicit There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm going to leave it with list because (a) it's not that import and (b) it needs the same recycling behaviour as a zero-length list in |
||
.x[] <- map(.x, .f, ...) | ||
.x | ||
} else { | ||
cli::cli_abort("Don't know how to modify {.obj_type_friendly {.x}}.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we use a "can't" form here?
Actually a "must" form works well I think:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think I should just ignore the fact it's a generic? It is unlikely that people have provided methods. Do you think I verify that there aren't methods on CRAN then remove the genericity? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm yes I think that is a good idea. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are five packages with
timbr's |
||
} | ||
|
||
.x | ||
} | ||
# TODO: Replace all the following methods with a generic strategy that | ||
# implements sane coercion rules for base vectors | ||
#' @export | ||
modify.integer <- function (.x, .f, ...) { | ||
.x[] <- map_int(.x, .f, ...) | ||
.x | ||
} | ||
#' @export | ||
modify.double <- function (.x, .f, ...) { | ||
.x[] <- map_dbl(.x, .f, ...) | ||
.x | ||
} | ||
#' @export | ||
modify.character <- function (.x, .f, ...) { | ||
.x[] <- map_chr(.x, .f, ...) | ||
.x | ||
} | ||
#' @export | ||
modify.logical <- function (.x, .f, ...) { | ||
.x[] <- map_lgl(.x, .f, ...) | ||
.x | ||
} | ||
#' @export | ||
modify.pairlist <- function(.x, .f, ...) { | ||
as.pairlist(map(.x, .f, ...)) | ||
} | ||
|
||
|
||
# modify_if --------------------------------------------------------------- | ||
|
||
#' @rdname modify | ||
#' @inheritParams map_if | ||
|
@@ -139,52 +116,16 @@ modify_if <- function(.x, .p, .f, ..., .else = NULL) { | |
#' @export | ||
modify_if.default <- function(.x, .p, .f, ..., .else = NULL) { | ||
where <- where_if(.x, .p) | ||
index <- seq_along(.x) | ||
.x <- modify_where(.x, where, .f, ...) | ||
|
||
.f <- as_mapper(.f, ...) | ||
for (i in index[where]) { | ||
list_slice2(.x, i) <- .f(.x[[i]], ...) | ||
} | ||
|
||
if (!is_null(.else)) { | ||
if (!is.null(.else)) { | ||
.else <- as_mapper(.else, ...) | ||
for (i in index[!where]) { | ||
list_slice2(.x, i) <- .else(.x[[i]], ...) | ||
} | ||
} | ||
|
||
.x | ||
} | ||
#' @export | ||
modify_if.integer <- function(.x, .p, .f, ..., .else = NULL) { | ||
modify_if_base(map_int, .x, .p, .true = .f, .false = .else, ...) | ||
} | ||
#' @export | ||
modify_if.double <- function(.x, .p, .f, ..., .else = NULL) { | ||
modify_if_base(map_dbl, .x, .p, .true = .f, .false = .else, ...) | ||
} | ||
#' @export | ||
modify_if.character <- function(.x, .p, .f, ..., .else = NULL) { | ||
modify_if_base(map_chr, .x, .p, .true = .f, .false = .else, ...) | ||
} | ||
#' @export | ||
modify_if.logical <- function(.x, .p, .f, ..., .else = NULL) { | ||
modify_if_base(map_lgl, .x, .p, .true = .f, .false = .else, ...) | ||
} | ||
|
||
modify_if_base <- function(.fmap, .x, .p, .true, .false = NULL, ..., .error_call = caller_env()) { | ||
where <- where_if(.x, .p, .error_call = .error_call) | ||
.x[where] <- .fmap(.x[where], .true, ...) | ||
|
||
if (!is.null(.false)) { | ||
.x[!where] <- .fmap(.x[!where], .false, ...) | ||
.x <- modify_where(.x, !where, .else, ...) | ||
} | ||
|
||
.x | ||
} | ||
|
||
# modify_at --------------------------------------------------------------- | ||
|
||
#' @rdname modify | ||
#' @inheritParams map_at | ||
#' @export | ||
|
@@ -195,76 +136,59 @@ modify_at <- function(.x, .at, .f, ...) { | |
#' @export | ||
modify_at.default <- function(.x, .at, .f, ...) { | ||
where <- where_at(.x, .at) | ||
modify_if(.x, where, .f, ...) | ||
} | ||
#' @export | ||
modify_at.integer <- function(.x, .at, .f, ...) { | ||
where <- where_at(.x, .at) | ||
.x[where] <- map_int(.x[where], .f, ...) | ||
.x | ||
} | ||
#' @export | ||
modify_at.double <- function(.x, .at, .f, ...) { | ||
where <- where_at(.x, .at) | ||
.x[where] <- map_dbl(.x[where], .f, ...) | ||
.x | ||
} | ||
#' @export | ||
modify_at.character <- function(.x, .at, .f, ...) { | ||
where <- where_at(.x, .at) | ||
.x[where] <- map_chr(.x[where], .f, ...) | ||
.x | ||
} | ||
#' @export | ||
modify_at.logical <- function(.x, .at, .f, ...) { | ||
where <- where_at(.x, .at) | ||
.x[where] <- map_lgl(.x[where], .f, ...) | ||
.x | ||
modify_where(.x, where, .f, ...) | ||
} | ||
|
||
# modify2 ----------------------------------------------------------------- | ||
|
||
#' @rdname modify | ||
#' @export | ||
modify2 <- function(.x, .y, .f, ...) { | ||
UseMethod("modify2") | ||
} | ||
#' @export | ||
modify2.default <- function(.x, .y, .f, ...) { | ||
modify2_base(map2, .x, .y, .f, ...) | ||
} | ||
# TODO: Improve genericity (see above) | ||
#' @export | ||
modify2.integer <- function(.x, .y, .f, ...) { | ||
modify2_base(map2_int, .x, .y, .f, ...) | ||
} | ||
#' @export | ||
modify2.double <- function(.x, .y, .f, ...) { | ||
modify2_base(map2_dbl, .x, .y, .f, ...) | ||
} | ||
#' @export | ||
modify2.character <- function(.x, .y, .f, ...) { | ||
modify2_base(map2_chr, .x, .y, .f, ...) | ||
} | ||
#' @export | ||
modify2.logical <- function(.x, .y, .f, ...) { | ||
modify2_base(map2_lgl, .x, .y, .f, ...) | ||
} | ||
|
||
modify2_base <- function(mapper, .x, .y, .f, ...) { | ||
.f <- as_mapper(.f, ...) | ||
out <- mapper(.x, .y, .f, ...) | ||
|
||
# if .x got recycled by map2 | ||
if (length(out) > length(.x)) { | ||
.x <- .x[rep(1L, length(out))] | ||
if (vec_is_list(.x) || is.data.frame(.x)) { | ||
out <- map2(vec_proxy(.x), .y, .f, ...) | ||
vec_restore(out, .x) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here regarding df restoration. |
||
} else if (vec_is(.x)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is simultaneously technically correct and also makes me sad that we generate the duplicated column names modify2(data.frame(x = 1), 1:3, \(x, y) x)
#> x x x
#> 1 1 1 1 |
||
map2_vec(.x, .y, .f, ..., .ptype = .x) | ||
} else if (is.null(.x) || is.list(.x)) { | ||
out <- map2(.x, .y, .f, ...) | ||
if (length(out) > length(.x)) { | ||
.x <- .x[rep(1L, length(out))] | ||
} | ||
.x[] <- out | ||
.x | ||
} else { | ||
cli::cli_abort("Don't know how to modify {.obj_type_friendly {.x}}.") | ||
} | ||
.x[] <- out | ||
.x | ||
} | ||
|
||
#' @rdname modify | ||
#' @export | ||
imodify <- function(.x, .f, ...) { | ||
modify2(.x, vec_index(.x), .f, ...) | ||
} | ||
|
||
|
||
# helpers ----------------------------------------------------------------- | ||
|
||
modify_where <- function(.x, .where, .f, ..., .error_call = caller_env()) { | ||
if (vec_is_list(.x) || is.data.frame(.x)) { | ||
out <- vec_proxy(.x) | ||
out[.where] <- map(out[.where], .f, ...) | ||
vec_restore(out, .x) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. df-restoration |
||
} else if (vec_is(.x)) { | ||
.x[.where] <- map_vec(.x[.where], .f, ..., .ptype = .x) | ||
.x | ||
} else if (is.null(.x) || is.list(.x)) { | ||
.x[.where] <- map(.x[.where], .f, ...) | ||
.x | ||
} else { | ||
cli::cli_abort( | ||
"Don't know how to modify {.obj_type_friendly {.x}}.", | ||
call = .error_call | ||
) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, yes, but also: 😬