2

I have a multiple lists in my environment(all start with "CDS_"). Each list is conducted of multiple sub lists.I want to call the lists one by one to apply a function for each of these objects. This is what I am trying:

lists<-grep("CDS_",names(.GlobalEnv),value=TRUE)  #Lists all objectrs staring with "CDS_"

for (i in seq_along(lists)){
  data<-do.call("list",mget(lists[i]))  #this line blends all sub lists into one list
  assign(paste("Df_", lists[i], sep = "_"), my_function(data)      # my_function requires a list with multiple sub lists
  }
 

but the issue is the do.call("list",mget(lists[i])) blends all sub lists into one. For example if there is a list with one sub list it returns the list but all sub lists go into one!

Any solutions how to make this work?

here is a sample to test:

#Defining my_function pulling out the sub list which contains "sample1"

my_function<-function(.data){           
  # pull out the undergraduate data
  grep("sample1", .data, value = TRUE) 
  
}

# 1st list
list_1 <- list(1:54,                             
               c("This","is","sample1","for","list1"),
               c("This","is","sample2","for","list1"),
               "Hi")

# 2nd list
list_2 <- list(51:120,                             
               c("This","is","sample1","for","list1"),
               c("This","is","sample2","for","list1"),
               "Bus")

# 3rd list
list_3 <- list(90:120, 
               letters[16:11],
               2025)





lists<-grep("list_",names(.GlobalEnv),value=TRUE)
for (i in seq_along(lists)){
  data<-do.call("list",mget(lists[i]))
  assign(paste("sample1_", lists[i], sep = ""), my_function(data))
} 
Hossein
  • 35
  • 5
  • 3
    It's easier to help you if you provide a [reproducible example](https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) with sample input and desired output that can be used to test and verify possible solutions. It's not clear why you need the `do.call` here. You seem to be calling `mget()` with only value value at a time. Messing with `mget` and `assign` is often a messy alternative to just sticking with a list. – MrFlick May 13 '22 at 17:56
  • @MrFlick added the sample code – Hossein May 13 '22 at 18:40

2 Answers2

4

As mentioned by @MrFlick, R has a ton of list functionality. It is usually the case that you are better off storing your lists in a list than trying to directly edit them in the environment. Here is one possible solution using base R:

l <- mget(ls(pattern = "^list_\\d$")) # store lists in a list

lapply(l, \(x) lapply(x, my_function))
$list_1
$list_1[[1]]
character(0)

$list_1[[2]]
[1] "sample1"

$list_1[[3]]
character(0)

$list_1[[4]]
character(0)


$list_2
$list_2[[1]]
character(0)

$list_2[[2]]
[1] "sample1"

$list_2[[3]]
character(0)

$list_2[[4]]
character(0)


$list_3
$list_3[[1]]
character(0)

$list_3[[2]]
character(0)

$list_3[[3]]
character(0)

Update

Sticking with base R to remove non-matches you could do:

lapply(l, \(x) Filter(length, lapply(x, my_function)))
$list_1
$list_1[[1]]
[1] "sample1"


$list_2
$list_2[[1]]
[1] "sample1"


$list_3
list()

A purrr solution would be:

library(purrr)

map(map_depth(l, 2, my_function), compact)
LMc
  • 12,577
  • 3
  • 31
  • 43
  • 2
    Great answer! Nice to see a new voice competently addressing `list`-related questions! – Gregor Thomas May 13 '22 at 18:47
  • Thanks for the suggestion @LMc, what if I want to pull out only the sub list that qualifies from each list and put them all in another list(each in a separate sub list)? – Hossein May 13 '22 at 18:59
  • @Hossein you are welcome, I have updated my post with a possible solution. – LMc May 13 '22 at 19:18
3

When you have lists of lists, and option is rapply, the recursive version of lapply.

my_function<-function(.data){           
  # pull out the undergraduate data
  grep("sample1", .data, value = TRUE) 
}

lists <- mget(ls(pattern = "^list_"))

rapply(lists, my_function, how = "list")
#> $list_1
#> $list_1[[1]]
#> character(0)
#> 
#> $list_1[[2]]
#> [1] "sample1"
#> 
#> $list_1[[3]]
#> character(0)
#> 
#> $list_1[[4]]
#> character(0)
#> 
#> 
#> $list_2
#> $list_2[[1]]
#> character(0)
#> 
#> $list_2[[2]]
#> [1] "sample1"
#> 
#> $list_2[[3]]
#> character(0)
#> 
#> $list_2[[4]]
#> character(0)
#> 
#> 
#> $list_3
#> $list_3[[1]]
#> character(0)
#> 
#> $list_3[[2]]
#> character(0)
#> 
#> $list_3[[3]]
#> character(0)

Created on 2022-05-13 by the reprex package (v2.0.1)


Edit

To answer to the OP's comment to another answer, to keep only the matches, save the rapply result and a lapply loop calling lengths, the list version of vector length is used to extract the matches.

r <- rapply(lists, my_function, how = "list")
lapply(r, \(x) x[lengths(x) > 0])
#> $list_1
#> $list_1[[1]]
#> [1] "sample1"
#> 
#> 
#> $list_2
#> $list_2[[1]]
#> [1] "sample1"
#> 
#> 
#> $list_3
#> list()

Created on 2022-05-13 by the reprex package (v2.0.1)

Rui Barradas
  • 70,273
  • 8
  • 34
  • 66