Reputation: 6956
I would like to replace a simple for loop by a purrr alternative. How can I code this and keep the original structure of my object
Here is my example:
my_list <- list(
a = list(
list(
aaa = c(1:3),
aab = c(4:6),
aac = c(7:9)),
list(
aaa = c(10:12),
aab = c(13:15),
aac = c(16:18)),
list(
aaa = c(19:21),
aab = c(22:24),
aac = c(25:27))
))
and my original solution
x <- purrr::map_lgl(my_list$a, .f = ~ !is.null(.x$aaa))
my_list1 <- my_list
for (i in which(x)) {
my_list1$a[[i]]$aaa <- 99
}
str(my_list1)
However, I would like to replace my for loop by a purrr-solution, as well. I tried something like this, but map always returns a new list instead of just updating it
my_list2 <- map(c(1, 3),
function(x){
my_list[["a"]][[x]][["aab"]] <- 99
return(my_list)
}
)
str(my_list2)
Modify should be more applicable in this context but I could'nt make it work either.
my_list3 <- purrr::map(
.x = which(x),
.f = function(i = .x) {
purrr::modify_in(.x = my_list,
.where = list("a", i, "aaa"),
.f = ~99)
}
)
str(my_list3)
The structure of the new list should look the same like the initial list:
# str(my_list)
List of 1
$ a:List of 3
..$ :List of 3
.. ..$ aaa: int [1:3] 1 2 3
.. ..$ aab: int [1:3] 4 5 6
.. ..$ aac: int [1:3] 7 8 9
..$ :List of 2
.. ..$ aab: int [1:3] 13 14 15
.. ..$ aac: int [1:3] 16 17 18
..$ :List of 3
.. ..$ aaa: int [1:3] 19 20 21
.. ..$ aab: int [1:3] 22 23 24
.. ..$ aac: int [1:3] 25 26 27
# str(my_list1) is correct
List of 1
$ a:List of 3
..$ :List of 3
.. ..$ aaa: num 99
.. ..$ aab: int [1:3] 4 5 6
.. ..$ aac: int [1:3] 7 8 9
..$ :List of 2
.. ..$ aab: int [1:3] 13 14 15
.. ..$ aac: int [1:3] 16 17 18
..$ :List of 3
.. ..$ aaa: num 99
.. ..$ aab: int [1:3] 22 23 24
.. ..$ aac: int [1:3] 25 26 27
# str(my_list2) or str (my_list3) is not correct
List of 2
$ :List of 1
..$ a:List of 3
.. ..$ :List of 3
.. .. ..$ aaa: num 99
.. .. ..$ aab: int [1:3] 4 5 6
.. .. ..$ aac: int [1:3] 7 8 9
.. ..$ :List of 2
.. .. ..$ aab: int [1:3] 13 14 15
.. .. ..$ aac: int [1:3] 16 17 18
.. ..$ :List of 3
.. .. ..$ aaa: int [1:3] 19 20 21
.. .. ..$ aab: int [1:3] 22 23 24
.. .. ..$ aac: int [1:3] 25 26 27
$ :List of 1
..$ a:List of 3
.. ..$ :List of 3
.. .. ..$ aaa: int [1:3] 1 2 3
.. .. ..$ aab: int [1:3] 4 5 6
.. .. ..$ aac: int [1:3] 7 8 9
.. ..$ :List of 2
.. .. ..$ aab: int [1:3] 13 14 15
.. .. ..$ aac: int [1:3] 16 17 18
.. ..$ :List of 3
.. .. ..$ aaa: num 99
.. .. ..$ aab: int [1:3] 22 23 24
.. .. ..$ aac: int [1:3] 25 26 27
Any hints where I go wrong with purrr?
Upvotes: 3
Views: 600
Reputation: 6234
Given that the list components to modify are located at the second nested level, you can also directly access them with modify_depth
:
library(purrr)
my_list <- modify_depth(my_list, 2, ~list_modify(.x, aaa = 99))
str(my_list)
#> List of 1
#> $ a:List of 3
#> ..$ :List of 3
#> .. ..$ aaa: num 99
#> .. ..$ aab: int [1:3] 4 5 6
#> .. ..$ aac: int [1:3] 7 8 9
#> ..$ :List of 3
#> .. ..$ aaa: num 99
#> .. ..$ aab: int [1:3] 13 14 15
#> .. ..$ aac: int [1:3] 16 17 18
#> ..$ :List of 3
#> .. ..$ aaa: num 99
#> .. ..$ aab: int [1:3] 22 23 24
#> .. ..$ aac: int [1:3] 25 26 27
NB: This also works for multiple list components at the first list level, (e.g. $a
, $b
, ...), without having to add an additional iteration step.
Edit: as listed in the documentation of modify_depth
:
modify_depth(x, 2, fun) is equivalent to x <- modify(x, ~ modify(., fun))
In order to replace only a selection of list components on the second nested level, we could rewrite the call to modify_depth
in a more verbose way as:
library(purrr)
## replace aaa only for components 1 and 3
modify_loc <- c(TRUE, FALSE, TRUE)
my_list <- modify(my_list, ~imodify(., ~if(modify_loc[.y]) list_modify(.x, aaa = 99) else .x))
str(my_list)
#> List of 1
#> $ a:List of 3
#> ..$ :List of 3
#> .. ..$ aaa: num 99
#> .. ..$ aab: int [1:3] 4 5 6
#> .. ..$ aac: int [1:3] 7 8 9
#> ..$ :List of 3
#> .. ..$ aaa: int [1:3] 10 11 12
#> .. ..$ aab: int [1:3] 13 14 15
#> .. ..$ aac: int [1:3] 16 17 18
#> ..$ :List of 3
#> .. ..$ aaa: num 99
#> .. ..$ aab: int [1:3] 22 23 24
#> .. ..$ aac: int [1:3] 25 26 27
Upvotes: 3
Reputation: 388982
One way would be using map
and change a single element in each list.
my_list[["a"]][x] <- purrr::map(my_list[["a"]][x], ~{.[["aaa"]] <- 99;.})
my_list
#$a
#$a[[1]]
#$a[[1]]$aaa
#[1] 99
#$a[[1]]$aab
#[1] 4 5 6
#$a[[1]]$aac
#[1] 7 8 9
#$a[[2]]
#$a[[2]]$aaa
#[1] 99
#$a[[2]]$aab
#[1] 13 14 15
#$a[[2]]$aac
#[1] 16 17 18
#......
which is same as using lapply
in base R
my_list[["a"]][x] <- lapply(my_list[["a"]][x], function(x) {x[["aaa"]] <- 99;x})
Upvotes: 2