map_depth()
calls map(.y, .f)
on all .y
at the specified .depth
in
.x
. modify_depth()
calls modify(.y, .f)
on .y
at the specified
.depth
in .x
.
map_depth(.x, .depth, .f, ..., .ragged = .depth < 0, .is_node = NULL)
modify_depth(.x, .depth, .f, ..., .ragged = .depth < 0, .is_node = NULL)
A list or atomic vector.
Level of .x
to map on. Use a negative value to
count up from the lowest level of the list.
map_depth(x, 0, fun)
is equivalent to fun(x)
.
map_depth(x, 1, fun)
is equivalent to x <- map(x, fun)
map_depth(x, 2, fun)
is equivalent to x <- map(x, \(y) map(y, fun))
A function, specified in one of the following ways:
A named function, e.g. mean
.
An anonymous function, e.g. \(x) x + 1
or function(x) x + 1
.
A formula, e.g. ~ .x + 1
. You must use .x
to refer to the first
argument. Only recommended if you require backward compatibility with
older versions of R.
A string, integer, or list, e.g. "idx"
, 1
, or list("idx", 1)
which
are shorthand for \(x) pluck(x, "idx")
, \(x) pluck(x, 1)
, and
\(x) pluck(x, "idx", 1)
respectively. Optionally supply .default
to
set a default value if the indexed element is NULL
or does not exist.
Additional arguments passed on to the mapped function.
We now generally recommend against using ...
to pass additional
(constant) arguments to .f
. Instead use a shorthand anonymous function:
This makes it easier to understand which arguments belong to which function and will tend to yield better error messages.
If TRUE
, will apply to leaves, even if they're not
at depth .depth
. If FALSE
, will throw an error if there are
no elements at depth .depth
.
A predicate function that determines whether an element is
a node (by returning TRUE
) or a leaf (by returning FALSE
). The
default value, NULL
, treats simple lists as nodes and everything else
(including richer objects like data frames and linear models) as leaves,
using vctrs::vec_is_list()
. To recurse into all objects built on lists
use is.list()
.
modify_tree()
for a recursive version of modify_depth()
that
allows you to apply a function to every leaf or every node.
Other map variants:
imap()
,
lmap()
,
map2()
,
map_if()
,
map()
,
modify()
,
pmap()
Other modify variants:
modify_tree()
,
modify()
# map_depth() -------------------------------------------------
# Use `map_depth()` to recursively traverse nested vectors and map
# a function at a certain depth:
x <- list(a = list(foo = 1:2, bar = 3:4), b = list(baz = 5:6))
x |> str()
#> List of 2
#> $ a:List of 2
#> ..$ foo: int [1:2] 1 2
#> ..$ bar: int [1:2] 3 4
#> $ b:List of 1
#> ..$ baz: int [1:2] 5 6
x |> map_depth(2, \(y) paste(y, collapse = "/")) |> str()
#> List of 2
#> $ a:List of 2
#> ..$ foo: chr "1/2"
#> ..$ bar: chr "3/4"
#> $ b:List of 1
#> ..$ baz: chr "5/6"
# Equivalent to:
x |> map(\(y) map(y, \(z) paste(z, collapse = "/"))) |> str()
#> List of 2
#> $ a:List of 2
#> ..$ foo: chr "1/2"
#> ..$ bar: chr "3/4"
#> $ b:List of 1
#> ..$ baz: chr "5/6"
# When ragged is TRUE, `.f()` will also be passed leaves at depth < `.depth`
x <- list(1, list(1, list(1, list(1, 1))))
x |> str()
#> List of 2
#> $ : num 1
#> $ :List of 2
#> ..$ : num 1
#> ..$ :List of 2
#> .. ..$ : num 1
#> .. ..$ :List of 2
#> .. .. ..$ : num 1
#> .. .. ..$ : num 1
x |> map_depth(4, \(x) length(unlist(x)), .ragged = TRUE) |> str()
#> List of 2
#> $ :List of 1
#> ..$ : int 1
#> $ :List of 2
#> ..$ :List of 1
#> .. ..$ : int 1
#> ..$ :List of 2
#> .. ..$ :List of 1
#> .. .. ..$ : int 1
#> .. ..$ :List of 2
#> .. .. ..$ : int 1
#> .. .. ..$ : int 1
x |> map_depth(3, \(x) length(unlist(x)), .ragged = TRUE) |> str()
#> List of 2
#> $ :List of 1
#> ..$ : int 1
#> $ :List of 2
#> ..$ :List of 1
#> .. ..$ : int 1
#> ..$ :List of 2
#> .. ..$ : int 1
#> .. ..$ : int 2
x |> map_depth(2, \(x) length(unlist(x)), .ragged = TRUE) |> str()
#> List of 2
#> $ :List of 1
#> ..$ : int 1
#> $ :List of 2
#> ..$ : int 1
#> ..$ : int 3
x |> map_depth(1, \(x) length(unlist(x)), .ragged = TRUE) |> str()
#> List of 2
#> $ : int 1
#> $ : int 4
x |> map_depth(0, \(x) length(unlist(x)), .ragged = TRUE) |> str()
#> int 5
# modify_depth() -------------------------------------------------
l1 <- list(
obj1 = list(
prop1 = list(param1 = 1:2, param2 = 3:4),
prop2 = list(param1 = 5:6, param2 = 7:8)
),
obj2 = list(
prop1 = list(param1 = 9:10, param2 = 11:12),
prop2 = list(param1 = 12:14, param2 = 15:17)
)
)
# In the above list, "obj" is level 1, "prop" is level 2 and "param"
# is level 3. To apply sum() on all params, we map it at depth 3:
l1 |> modify_depth(3, sum) |> str()
#> List of 2
#> $ obj1:List of 2
#> ..$ prop1:List of 2
#> .. ..$ param1: int 3
#> .. ..$ param2: int 7
#> ..$ prop2:List of 2
#> .. ..$ param1: int 11
#> .. ..$ param2: int 15
#> $ obj2:List of 2
#> ..$ prop1:List of 2
#> .. ..$ param1: int 19
#> .. ..$ param2: int 23
#> ..$ prop2:List of 2
#> .. ..$ param1: int 39
#> .. ..$ param2: int 48
# modify() lets us pluck the elements prop1/param2 in obj1 and obj2:
l1 |> modify(c("prop1", "param2")) |> str()
#> List of 2
#> $ obj1: int [1:2] 3 4
#> $ obj2: int [1:2] 11 12
# But what if we want to pluck all param2 elements? Then we need to
# act at a lower level:
l1 |> modify_depth(2, "param2") |> str()
#> List of 2
#> $ obj1:List of 2
#> ..$ prop1: int [1:2] 3 4
#> ..$ prop2: int [1:2] 7 8
#> $ obj2:List of 2
#> ..$ prop1: int [1:2] 11 12
#> ..$ prop2: int [1:3] 15 16 17
# modify_depth() can be with other purrr functions to make them operate at
# a lower level. Here we ask pmap() to map paste() simultaneously over all
# elements of the objects at the second level. paste() is effectively
# mapped at level 3.
l1 |> modify_depth(2, \(x) pmap(x, paste, sep = " / ")) |> str()
#> List of 2
#> $ obj1:List of 2
#> ..$ prop1:List of 2
#> .. ..$ : chr "1 / 3"
#> .. ..$ : chr "2 / 4"
#> ..$ prop2:List of 2
#> .. ..$ : chr "5 / 7"
#> .. ..$ : chr "6 / 8"
#> $ obj2:List of 2
#> ..$ prop1:List of 2
#> .. ..$ : chr "9 / 11"
#> .. ..$ : chr "10 / 12"
#> ..$ prop2:List of 3
#> .. ..$ : chr "12 / 15"
#> .. ..$ : chr "13 / 16"
#> .. ..$ : chr "14 / 17"