0

I am trying to assign elements to a list in a nest purrr::map call -- this should basically be the same as a nested for loop:

res <- list()
for (i in 1:4) {
  for (j in letters[1:3]) {
    res[[paste(i,j)]] <- paste(i,j)
  }
}

str(res)
#> List of 12
#>  $ 1 a: chr "1 a"
#>  $ 1 b: chr "1 b"
#>  $ 1 c: chr "1 c"
#>  $ 2 a: chr "2 a"
#>  $ 2 b: chr "2 b"
#>  $ 2 c: chr "2 c"
#>  $ 3 a: chr "3 a"
#>  $ 3 b: chr "3 b"
#>  $ 3 c: chr "3 c"
#>  $ 4 a: chr "4 a"
#>  $ 4 b: chr "4 b"
#>  $ 4 c: chr "4 c"

However, when I attempt to convert this to purrr, the results get printed to the console, but are never stored in the list object res_purrr?

library(purrr)
res_purrr <- list()

map(1:4, function(i)
  map(letters[1:3], function(j)
    res_purrr[[paste(i,j)]] <- paste(i,j)
  )
)

res_purrr
#> list()

Running the same code with walk returns the same empty res_purrr object.

JasonAizkalns
  • 20,243
  • 8
  • 57
  • 116
  • 2
    You need to assign the result back to `res_purrr` to replace it. If you want `res_purrr` to be updated in place, you can use `<<-`, but it is almost never recommended. – acylam Jan 23 '19 at 16:46
  • 3
    Other people have correctly pointed out the failure to assign the result, but more broadly, you're using `for` loop logic on a functional programming tool, which won't give you the results you want in a lot of cases. For instance, you aren't actually getting the list element names that you want. You don't use tools like `map` in a way that does assignments to external objects inside the "looping" like that. Also, you're getting a nested list, not a single list of 12. – joran Jan 23 '19 at 16:48
  • @avid_useR I am trying to accomplish same as original poster here. Can you clarify how to assign the result back to the original list (res_purrr) here other than using not recommended <<- ? – user42485 Aug 19 '20 at 13:53
  • @user42485 The accepted answer would be the solution to OP's question. If it doesn't answer your question, I would search SO or ask your own question (make sure no one has already asked it). – acylam Aug 19 '20 at 14:55

2 Answers2

2

I would do the following :

library(purrr)
res_purrr <- expand.grid(1:4,letters[1:3],stringsAsFactors = FALSE) %>%
  pmap(paste) %>%
  set_names()
str(res_purrr)
# List of 12
# $ 1 a: chr "1 a"
# $ 2 a: chr "2 a"
# $ 3 a: chr "3 a"
# $ 4 a: chr "4 a"
# $ 1 b: chr "1 b"
# $ 2 b: chr "2 b"
# $ 3 b: chr "3 b"
# $ 4 b: chr "4 b"
# $ 1 c: chr "1 c"
# $ 2 c: chr "2 c"
# $ 3 c: chr "3 c"
# $ 4 c: chr "4 c"

This question is relevant : purrr map equivalent of nested for loop

moodymudskipper
  • 46,417
  • 11
  • 121
  • 167
  • I like this -- curious, can you think of any top-of-mind potential drawbacks if the function (`paste`) and/or assignment is more complicated? – JasonAizkalns Jan 23 '19 at 16:54
  • you can use the formula notation instead, so the line could become `pmap(~paste(sqrt(..1),"foo",toupper(..2)))` or any function instead of paste – moodymudskipper Jan 23 '19 at 16:56
  • Thanks. I could post another question, but might be easier for you to just modify this answer -- if we use `pmap(~paste(sqrt(..1),"foo",toupper(..2)))`, is it possible to have `set_names` refer back to the original grid? That is, the name of the first result would be `1 a` NOT `1 foo A`. – JasonAizkalns Jan 23 '19 at 17:12
  • Everything is possible, I'll edit my answer tomorrow :) – moodymudskipper Jan 23 '19 at 17:48
1

Using your code, we can simply add res_purr <-

res_purrr <- map(1:4, function(i)
  map(letters[1:3], function(j)
    res_purrr[[paste(i,j)]] <- paste(i,j)
  )
)
str(res_purrr)  
# List of 4
# $ :List of 3
# ..$ : chr "1 a"
# ..$ : chr "1 b"
# ..$ : chr "1 c"
# ...

Edit

If the question was about reproducing res as opposed to failing to store the result of map, here as a (similar but) alternative approach to @Moody_Mudskipper's answer:

x <- do.call(paste, expand.grid(1:3, letters[1:4], stringsAsFactors = FALSE))
res <- setNames(as.list(x), x)
str(res)  
# List of 12
# $ 1 a: chr "1 a"
# $ 2 a: chr "2 a"
# ...

And here with a custom function instead of paste

x <- do.call(function (Var1,Var2) paste(sqrt(Var1),toupper(Var2)), 
             expand.grid(1:4, letters[1:3], stringsAsFactors = FALSE))
res <- setNames(as.list(x), x)
str(res) 
# List of 12
# $ 1 A               : chr "1 A"
# $ 1.4142135623731 A : chr "1.4142135623731 A"
# $ 1.73205080756888 A: chr "1.73205080756888 A"
# $ 2 A               : chr "2 A"
# ...
niko
  • 5,253
  • 1
  • 12
  • 32