Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# purrr (development version)

* `list_flatten()` gains an `is_node` parameter taking a predicate function that determines whether an input element is a node or a leaf (@salim-b, #1179).

* `in_parallel()` now accepts objects, including helper functions, supplied to `...` for all locally-defined functions (#1208).

* `in_parallel()` now works in conjunction with string and list values supplied to the `.progress` argument of map functions (#1203).
Expand Down
23 changes: 21 additions & 2 deletions R/list-flatten.R
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#' `"check_unique"`. See [vctrs::vec_as_names()] for the meaning of these
#' options.
#' @inheritParams rlang::args_dots_empty
#' @inheritParams modify_tree
#' @return A list of the same type as `x`. The list might be shorter
#' if `x` contains empty lists, the same length if it contains lists
#' of length 1 or no sub-lists, or longer if it contains lists of
Expand Down Expand Up @@ -40,13 +41,31 @@
#' x |> list_flatten() |> names()
#' x |> list_flatten(name_spec = "{outer}") |> names()
#' x |> list_flatten(name_spec = "{inner}") |> names()
#'
#' # Set `is_node = is.list` to also flatten richer objects built on lists like
#' # data frames and linear models
#' df <- data.frame(x = 1:3, y = 4:6)
#' x <- list(
#' a_string = "something",
#' a_list = list(1:3, "else"),
#' a_df = df
#' )
#' x |> list_flatten(is_node = is.list)
#'
#' # Note that objects that are already "flat" retain their classes
#' list_flatten(df, is_node = is.list)
list_flatten <- function(
x,
...,
is_node = NULL,
name_spec = "{outer}_{inner}",
name_repair = c("minimal", "unique", "check_unique", "universal")
) {
obj_check_list(x)
is_node <- as_is_node(is_node)
if (!is_node(x)) {
cli::cli_abort("{.arg x} must be a node.")
}

check_dots_empty()
check_string(name_spec)

Expand All @@ -55,7 +74,7 @@ list_flatten <- function(

# Unclass S3 lists to avoid their coercion methods. Wrap atoms in a
# list of size 1 so the elements can be concatenated in a single list.
proxy <- map_if(proxy, obj_is_list, unclass, .else = list)
proxy <- map_if(proxy, is_node, unclass, .else = list)

out <- list_unchop(
proxy,
Expand Down
21 changes: 21 additions & 0 deletions man/list_flatten.Rd

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

2 changes: 1 addition & 1 deletion tests/testthat/_snaps/list-flatten.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
list_flatten(1:2)
Condition
Error in `list_flatten()`:
! `x` must be a list, not an integer vector.
! `x` must be a node.

5 changes: 5 additions & 0 deletions tests/testthat/test-list-flatten.R
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,8 @@ test_that("list_flatten() works with vctrs::list_of()", {
list_of(1, 2, 3)
)
})

test_that("list_flatten() honors its is_node param", {
expect_equal(list_flatten(list(mtcars)), list(mtcars))
expect_equal(list_flatten(list(mtcars), is_node = is.list), as.list(mtcars))
})
Loading