简体   繁体   中英

purrr map equivalent of nested for loop

What is the purrr::map equivalent of:

for (i in 1:4) {
  for (j in 1:6) {
    print(paste(i, j, sep = "-"))
  }
}

OR

lapply(1:4, function(i) 
  lapply(1:6, function(j) 
    print(paste(i, j, sep = "-"))))

Conceptually, what I'm not getting is how to refer to the outer loop in the inner map function.

map(1:4, ~ map(1:6, ~ print(paste(.x, ????, sep = "-")))

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"

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"

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM