Home > Back-end >  Nested for loops in R Using Tidyverse, where iterator is generated within the first loop
Nested for loops in R Using Tidyverse, where iterator is generated within the first loop

Time:09-22

I'm working on a little problem in R where I'm curious if I can use purrr to iterate over excel files and sheets.

I've seen a lot of examples where map() is being given the iterable object directly, i.e. map(1:6, function(x)... but I'm not sure if/how to do it when I want to generate one of the iterators within the first map call.

Take this example where we have a folder of excel files, and we want to run the same function on each sheet. So we need two loops, one to loop through the files, and one to iterate through the sheets.

    library(tidyverse)
    library(readxl)

    fileList <- list.files()

    
    customFunction <- function(xlsheet, filePath){
        return(1)
    }

    output <- list()
    i <- 1 

    for (file in fileList){
      # get a list of excel sheets in each file
      sheet_list <- excel_sheets(file)
        for (sheet in sheet_list){
          # apply customFunction to each sheet of each file
          output[[i]] <- customFunction(sheet, file)
          i <- i   1
       }
    }

I think where I'm getting stuck is that I need arguments from both the first and the second loop in each call of customFunction().

Looking at an example from @r2evans in another question, it seems like they're describing something like this:

map(fileList, ~ map(excel_sheets(.x), ~ customFunction(.x, .y)))

But that returns an error in my actual code (and in this example it returns a nested list instead of a single list like the for loop, noting my example won't fail if the sheet and path aren't correctly passed to customFunction)

Error in is_string(path) : the ... list contains fewer than 2 elements

And I'm honestly a little lost with the .x and .y pronouns

Finally, if this is a silly thing to try to do with purrr and the for loop is generally a better solution, that's great feedback too.

CodePudding user response:

Using .x and .y can be confusing when you have nested maps. I usually prefer to use an anonymous function to be clear and yes nested map's would return nested list. You can use flatten to get one big list like the for loop or use unlist with recursive = FALSE.

library(purrr)

flatten(map(fileList, function(file) 
        map(excel_sheets(file), function(sheet) 
        customFunction(file, sheet))))
  • Related