Search code examples
nested-loopspurrr

Using purrr:map2 to transfer row names from one nested list to another


I can't seem to find a solution to the following problem: I have two nested lists (l1 and l2) and I want to transfer information in l2 as row names for the matrices in l1. To this end, I need to use nested map2 commands, however I either get no row names at all in l1 (using walk2 or modify2 instead of map2) or the whole matrix gets replaced by the test that I wanted to have as row names.

Here's my mwe:

library(tidyverse)

mat <- matrix(1:225, nrow = 15)
colnames(mat) <- LETTERS[1:15]
str(mat)
#>  int [1:15, 1:15] 1 2 3 4 5 6 7 8 9 10 ...
#>  - attr(*, "dimnames")=List of 2
#>   ..$ : NULL
#>   ..$ : chr [1:15] "A" "B" "C" "D" ...

rnames <- paste("This is name", 1:15)

l1 <- list(
  "sub1" = list(
    "a1" = mat,
    "a2" = mat,
    "a3" = mat
  ),
  "sub2" = list(
    "a1" = mat,
    "a2" = mat,
    "a3" = mat
  ))

l2 <- list(
  "sub1" = list(
    "a1" = rnames,
    "a2" = rnames,
    "a3" = rnames
  ),
  "sub2" = list(
    "a1" = rnames,
    "a2" = rnames,
    "a3" = rnames
  ))

l3 <- map2(.x = l1,
           .y = l2,
           .f = function(sol,csl) {
             map2(.x = sol,
                  .y = csl,
                  .f = function(so, cs) {
                    rownames(so) <- cs
                  }
             )
           }
)

l1[[1]][[1]] %>% view()
l2[[1]][[1]] %>% view()
l3[[1]][[1]] %>% view()

Does anyone have an idea how to solve this issue?


Solution

  • Your call to map2() isn't working as expected because your inner function does not return so, but rather names(so). This is because names(so) is the last valued assigned within your function.

    To get the behaviour you require, you just need to explicitly evaluate so within the last line of your function, so that it returns so.

    library(purrr)
    
    l3 <- map2(.x = l1,
               .y = l2,
               .f = function(sol,csl) {
                 map2(.x = sol,
                      .y = csl,
                      .f = function(so, cs) {
                        rownames(so) <- cs
                        ## Evaluating so will make this work
                        so   
                      }
                 )
               }
    )
    

    Created on 2023-09-22 with reprex v2.0.2

    Reproducible error:

    Here's a tiny reprex that shows why things weren't going as you expected before:

    implicit_return <- \(x) names(x) <- "Don't output this"
    
    explicit_return <- \(x){
      names(x) <- "Don't output this"
      x
    }
    
    unexpected_output <- implicit_return(1)
    desired_output <- explicit_return(1)
    
    
    ## The implicit return of this function is the last assigned value, 
    ## which is the value we assigned to the rownames, rather than a renamed object
    unexpected_output
    #> [1] "Don't output this"
    
    ## Evaluationg the object gives us the expected behaviour
    desired_output
    #> Don't output this 
    #>                 1
    

    Created on 2023-09-22 with reprex v2.0.2