What is the purrr::map equivalent of:
for (i in 1:4) {
for (j in 1:6) {
print(paste(i, j, sep = \"-\"))
}
}
OR
lap
Here is an addition to the already very good answers and answer-comments. I wanted to make a single purr-like function that accomplishes the OP's goals. So I made a loop_map
function that behaves analogously to the main Purrr map
functions.
loop_map <- function(why, ecks, fun) {
# 2: for every call of this (the previous .f) the new .f is called for each
# value of ecks, supplied the same value of why each time
iterate_over_x = function(x_in,y_in,fun_in){
return(pmap(.l = list(x = x_in), .f = fun_in ,y = y_in ) %>%
set_names(nm = as.character(x_in)))
}
# 1: this ".f" argument is called once for each element of why, and is
# supplied one value of why and every value of ecks each time
pmap(.l = list(y_in = why), .f = iterate_over_x, x_in = ecks, fun_in = fun) %>%
set_names(nm = as.character(why))
}
my_paste <- function(x,y) {
paste(x,y)
}
loop_map(list("a","b"),list("c","d"),my_paste)
As a bonus I named the output so that one can index it more easily, or somehow convert it to a dataframe. I would like to improve this function by adding capabilities to loop over arbitrarily many input lists, and possibly to use functions that take ...
arguments (right now everything has to be named). If anyone has an idea for how to do this feel free to let me know.
Just Running through this now.
walk(1:4,~ walk(1:6, ~ print(paste(.x, .y, sep = "-")),.y=.x))
[1] "1-1"
[1] "2-1"
[1] "3-1"
[1] "4-1"
[1] "5-1"
[1] "6-1"
[1] "1-2"
and
purrr::pwalk(expand.grid(1:4,1:6),~print(paste(.x, .y, sep = "-")))
[1] "1-1"
[1] "2-1"
[1] "3-1"
[1] "4-1"
[1] "1-2"
but to match your nested for loops exactly it fiddled and this works.
for (i in 1:4) {
for (j in 1:6) {
print(paste(i, j, sep = "-"))
}
}
[1] "1-1"
[1] "1-2"
[1] "1-3"
[1] "1-4"
[1] "1-5"
[1] "1-6"
[1] "2-1"
purrr::pwalk(expand.grid(1:6,1:4),~print(paste(.y, .x, sep = "-")))
[1] "1-1"
[1] "1-2"
[1] "1-3"
[1] "1-4"
[1] "1-5"
[1] "1-6"
[1] "2-1"
#or even a map of this
walk(1:4,~ walk(1:6, ~ print(paste(.y, .x, sep = "-")),.y=.x))
I have yet to figure out why the .y=.x
is at the end though.
As @r2evans points out, the .x
from your first call is masked. however you can create a lambda function that takes 2 parameters .x
and .y
, and assign the previous .x
to the new .y
through the ...
argument.
I'll use walk
rather than map
as in this case you're only interested in side effects (printing)
walk(1:4,~ walk(1:6, ~ print(paste(.x, .y, sep = "-")),.y=.x))
Another option is to use expand.grid
to lay out the combinations, and then iterate on those with pwalk
(or pmap
in other circumstances)
purrr::pwalk(expand.grid(1:4,1:6),~print(paste(.x, .y, sep = "-")))
Output in both cases:
[1] "1-1"
[1] "2-1"
[1] "3-1"
[1] "4-1"
[1] "5-1"
[1] "6-1"
[1] "1-2"
[1] "2-2"
[1] "3-2"
[1] "4-2"
[1] "5-2"
[1] "6-2"
[1] "1-3"
[1] "2-3"
[1] "3-3"
[1] "4-3"
[1] "5-3"
[1] "6-3"
[1] "1-4"
[1] "2-4"
[1] "3-4"
[1] "4-4"
[1] "5-4"
[1] "6-4"
The use of function formulas (~) is a little limited when trying to nest like this, since it is perfectly unclear which level of map
you are attempting to reference. (Well, that's not correct. It's perfectly clear to me that it is referencing inside-out, and since they both use the same nomenclature, the outer variables are being masked by the inner variables.)
I think your best way around it is to not use the formula method, instead using immediate/anonymous (or predefined) functions:
library(purrr)
str(map(1:2, function(x) map(1:3, function(y) paste(x, y, sep = "-"))))
# List of 2
# $ :List of 3
# ..$ : chr "1-1"
# ..$ : chr "1-2"
# ..$ : chr "1-3"
# $ :List of 3
# ..$ : chr "2-1"
# ..$ : chr "2-2"
# ..$ : chr "2-3"