Search code examples
rlisttidyverseapplypurrr

Function applied to each element of a list and returns a list of list in R


For a list in R, I want to apply a function that accepts each element in each list and returns three numbers, which is the input value plus 1, 2, and 3. I want to create a list of list such that for each component of the original list, a list is returned where each component of this list is the output from each element of the original list. For all components of the list, I get a list of list.

Specifically, For list lst <- list(a = 1:3, b = 4:5, c = 6:8),

I want a list as follows:

> lst2
[[1]]
[[1]][[1]]
[1] 2 3 4

[[1]][[2]]
[1] 3 4 5

[[1]][[3]]
[1] 4 5 6


[[2]]
[[2]][[1]]
[1] 5 6

[[2]][[2]]
[1] 6 7


[[3]]
[[3]][[1]]
[1] 7 8 9

[[3]][[2]]
[1]  8  9 10

[[3]][[3]]
[1]  9 10 11

I want to complete the task using map function or related functions in purr(r) package(s) in tidyverse ecosystem. Using Map or apply family of functions in base r is fine. I just do not want to use for loop.


Solution

  • Using lapply or purrr::map you could do:

    lst <- list(a = 1:3, b = 4:5, c = 6:8)
    
    lst |>
      unname() |>
      lapply(\(el) lapply(el, \(x) seq(length(el)) + x))
    #> [[1]]
    #> [[1]][[1]]
    #> [1] 2 3 4
    #> 
    #> [[1]][[2]]
    #> [1] 3 4 5
    #> 
    #> [[1]][[3]]
    #> [1] 4 5 6
    #> 
    #> 
    #> [[2]]
    #> [[2]][[1]]
    #> [1] 5 6
    #> 
    #> [[2]][[2]]
    #> [1] 6 7
    #> 
    #> 
    #> [[3]]
    #> [[3]][[1]]
    #> [1] 7 8 9
    #> 
    #> [[3]][[2]]
    #> [1]  8  9 10
    #> 
    #> [[3]][[3]]
    #> [1]  9 10 11
    

    EDIT For the approach you tried with map in your comment you have to take care that myFunA takes two arguments, i.e. you need to pass the length of the outer list to myFunA.

    myFunA <- function(x, n) seq_len(n) + x
    myFunB <- function(y) purrr::map(y, myFunA, n = length(y))
    
    purrr::map(unname(lst), myFunB)