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))
}
CodePudding user response:
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)
CodePudding user response:
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)