Skip to content

Commit

Permalink
pmap_qto (#9)
Browse files Browse the repository at this point in the history
Changes:
	- Introduces a pmap_qto to compliment the map_qto function introduced by @elipousson
    - map_qto should be slightly more performant (function building is done outside the map call)
    - collapse and sep are now passed to .type functions where relevant
  • Loading branch information
ElianHugh authored Dec 24, 2023
1 parent beef3ed commit 0f454d5
Show file tree
Hide file tree
Showing 7 changed files with 409 additions and 42 deletions.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

S3method(print,quarto_block)
export(map_qto)
export(pmap_qto)
export(qto_attributes)
export(qto_block)
export(qto_callout)
Expand Down
53 changes: 53 additions & 0 deletions R/import-standalone-purrr.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# nocov start

#' all functions here are copied from the
#' `standalone-purrr.R` script in rlang:
#' https://github.com/r-lib/rlang/blob/main/R/standalone-purrr.R
#' @noRd
map <- function(.x, .f, ...) {
.f <- as_function(.f, env = global_env())
lapply(.x, .f, ...)
}

#' @noRd
map_chr <- function(.x, .f, ...) {
.rlang_purrr_map_mold(.x, .f, character(1L), ...)
}

#' @noRd
map_int <- function(.x, .f, ...) {
.rlang_purrr_map_mold(.x, .f, integer(1), ...)
}

#' @noRd
.rlang_purrr_map_mold <- function(.x, .f, .mold, ...) {
.f <- as_function(.f, env = global_env())
out <- vapply(.x, .f, .mold, ..., USE.NAMES = FALSE)
names(out) <- names(.x)
out
}

#' @noRd
pmap <- function(.l, .f, ...) {
.f <- as.function(.f)
args <- .rlang_purrr_args_recycle(.l)
do.call("mapply", c(
FUN = list(quote(.f)),
args, MoreArgs = quote(list(...)),
SIMPLIFY = FALSE, USE.NAMES = FALSE
))
}

#' @noRd
.rlang_purrr_args_recycle <- function(args) {
lengths <- map_int(args, length)
n <- max(lengths)

stopifnot(all(lengths == 1L | lengths == n))
to_recycle <- lengths == 1L
args[to_recycle] <- map(args[to_recycle], function(x) rep.int(x, n))

args
}

# nocov end
136 changes: 97 additions & 39 deletions R/map.R
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
#' map, map_chr, and .rlang_purrr_map_mold are copied from the
#' `standalone-purrr.R` script in rlang:
#' https://github.com/r-lib/rlang/blob/main/R/standalone-purrr.R
#' @noRd
map <- function(.x, .f, ...) {
.f <- as_function(.f, env = global_env())
lapply(.x, .f, ...)
partial_qto_func <- function(f, collapse, sep) {
if (identical(f, qto_callout)) {
function(...) f(...)
} else if ((identical(f, qto_div))) {
function(...) f(..., collapse = collapse)
} else {
function(...) f(..., collapse = collapse, sep = sep)
}
}

#' @noRd
map_chr <- function(.x, .f, ...) {
.rlang_purrr_map_mold(.x, .f, character(1L), ...)
}

#' @noRd
.rlang_purrr_map_mold <- function(.x, .f, .mold, ...) {
.f <- as_function(.f, env = global_env())
out <- vapply(.x, .f, .mold, ..., USE.NAMES = FALSE)
names(out) <- names(.x)
out
resolve_mapping_function <- function(f = NULL,
type = NULL,
collapse = NULL,
sep = NULL,
call = NULL) {
f <- f %||% switch(type,
block = partial_qto_func(qto_block, collapse, sep),
div = partial_qto_func(qto_div, collapse, sep),
callout = partial_qto_func(qto_callout, collapse, sep),
heading = partial_qto_func(qto_heading, collapse, sep),
)
if (!is_function(f)) {
f <- as_function(f, call = call)
}
f
}

#' Apply a function to each element of a vector and return Quarto block vector
Expand All @@ -35,7 +40,8 @@ map_chr <- function(.x, .f, ...) {
#' "heading".
#' @param .sep,.collapse Additional parameters passed to [qto_block()] if .f
#' does not return a quarto block class object. Ignored if .f does return a
#' quarto block class object.
#' quarto block class object. Also passed to the relevant .type function if it supports
#' the collapse and/or sep parameters.
#' @inheritParams rlang::args_error_context
#' @examples
#' qto_list <- map_qto(
Expand All @@ -45,37 +51,89 @@ map_chr <- function(.x, .f, ...) {
#'
#' qto_block(qto_list)
#'
#' @seealso [quartools::pmap_qto()], [purrr::map()]
#' @export
map_qto <- function(.x,
.f = NULL,
...,
.type = c("block", "div", "callout", "heading"),
.sep = "",
.collapse = "",
.call = caller_env()) {
call = caller_env()) {
.type <- arg_match(.type, error_call = call)

.f <- resolve_mapping_function(
f = .f,
type = .type,
collapse = .collapse,
sep = .sep,
call = call
)
map(
.x,
\(x) {
.f <- .f %||% switch(.type,
block = qto_block,
div = qto_div,
callout = qto_callout,
heading = qto_heading
)

if (!rlang::is_function(.f)) {
.f <- rlang::as_function(.f, call = call)
}

function(x) {
x <- .f(x, ...)

if (inherits(x, "quarto_block")) {
return(x)
}

qto_block(x, sep = .sep, collapse = .collapse, call = .call)
if (inherits(x, "quarto_block")) return(x)
qto_block(x, sep = .sep, collapse = .collapse, call = call)
}
)
}

#' Map over multiple inputs simultaenously and return Quarto block vector
#'
#' [pmap_qto()] loops a list of vectors over a package function defined by .type or a custom
#' function that returns a quarto block output. This function always returns a
#' list of quarto block objects.
#'
#' @param .l An input vector.
#' @inheritParams rlang::args_error_context
#' @inheritParams map_qto
#' @examples
#' qto_list <- pmap_qto(
#' list(
#' list("Answer:", "Answer:", "Answer:"),
#' list("Yes", "No", "Yes")
#' )
#' )
#' qto_block(qto_list)
#'
#' qto_list <- pmap_qto(
#' mtcars[seq(3L), seq(3L)],
#' function(mpg, cyl, disp) {
#' qto_li(
#' .list = list(
#' sprintf("mpg is: %s", mpg),
#' sprintf("cyl is: %s", cyl),
#' sprintf("disp is: %s", disp)
#' )
#' )
#' }
#' )
#' qto_block(qto_list)
#'
#' @seealso [quartools::map_qto()], [purrr::pmap()]
#' @export
pmap_qto <- function(.l,
.f = NULL,
...,
.type = c("block", "div", "callout", "heading"),
.sep = "",
.collapse = "",
call = caller_env()) {
.type <- arg_match(.type, error_call = call)
.f <- resolve_mapping_function(
f = .f,
type = .type,
collapse = .collapse,
sep = .sep,
call = call
)
pmap(
.l,
function(...) {
x <- .f(...)
if (inherits(x, "quarto_block")) return(x)
qto_block(x, sep = .sep, collapse = .collapse, call = call)
},
...
)
}
10 changes: 7 additions & 3 deletions man/map_qto.Rd

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

70 changes: 70 additions & 0 deletions man/pmap_qto.Rd

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

Loading

0 comments on commit 0f454d5

Please sign in to comment.