Reputation: 6210
How can I go from
x <- list(p1 = list(type='A',score=list(c1=10,c2=8,c3=data.frame(a=1, b=3, c=5))),
p2 = list(type='B',score=list(c1=9,c2=9,c3=data.frame(a=2, b=2))),
p3 = list(type='B',score=list(c1=9,c2=7,c3=data.frame(a=2, b=2))))
to a list without the "c3" elements that are data frames?
Preferably in a tidyverse
-friendly way or something I can put in the middle of a pipeline.
I've already tried list.remove, nested lapply, rapply, Filter, but can't seem to get them to work... and I don't want to unlist my nested list structure.
(Edit: sorry, I had a typo in the sample data in my original question (see below), but great if your solution works in both cases!)
x <- list(p1 = list(type='A',score=list(c1=10,c2=8,c3=data.frame(a=1, b=3, c=5))),
p2 = list(type='B',score=list(c1=9,c2=9,c3=data.frame(a=2, b=2)),
p3 = list(type='B',score=list(c1=9,c2=7,c3=data.frame(a=2, b=2)))))
Upvotes: 3
Views: 2178
Reputation: 6234
Here is another alternative using rrapply()
in the rrapply
-package that works for arbitrary levels of nesting:
library(rrapply)
x1 <- rrapply(x, condition = function(x, .xparents) !any(.xparents == "c3"), how = "prune")
str(x1)
#> List of 3
#> $ p1:List of 2
#> ..$ type : chr "A"
#> ..$ score:List of 2
#> .. ..$ c1: num 10
#> .. ..$ c2: num 8
#> $ p2:List of 2
#> ..$ type : chr "B"
#> ..$ score:List of 2
#> .. ..$ c1: num 9
#> .. ..$ c2: num 9
#> $ p3:List of 2
#> ..$ type : chr "B"
#> ..$ score:List of 2
#> .. ..$ c1: num 9
#> .. ..$ c2: num 7
Upvotes: 2
Reputation: 15062
This is the right scenario to use modify_depth
, which functions as a shortcut for chains of modify
to access deep nested lists. modify
has an advantage over map
in this problem because it will preserve the type of the input instead of coercing everything to lists, which may be relevant if you have vector elements of your list structure.
Using your given input (with a p3
element inside rather than on the same level as p2
), the dataframe elements at the second and third levels are discard
ed as below. In order to search all levels of the nested list, we can set up a while
loop to iterate through the levels, discarding dataframes as we go. We need .ragged = TRUE
to deal with errors with list depth. This version searches bottom up but you could change it to search top down as well.
library(tidyverse)
x <- list(
p1 = list(type = "A", score = list(c1 = 10, c2 = 8, c3 = data.frame(a = 1, b = 3, c = 5))),
p2 = list(
type = "B", score = list(c1 = 9, c2 = 9, c3 = data.frame(a = 2, b = 2)),
p3 = list(type = "B", score = list(c1 = 9, c2 = 7, c3 = data.frame(a = 2, b = 2)))
)
)
remove_dataframes <- function(input_list) {
current_list <- input_list
current_depth <- vec_depth(current_list)
# current_depth <- max_depth
while (current_depth > 1) {
current_list <- modify_depth(
.x = current_list,
.depth = current_depth,
.f = ~ discard(., is.data.frame),
.ragged = TRUE
)
current_depth <- current_depth - 1
}
return(current_list)
}
x %>%
remove_dataframes %>%
glimpse
#> List of 2
#> $ p1:List of 2
#> ..$ type : chr "A"
#> ..$ score:List of 2
#> .. ..$ c1: num 10
#> .. ..$ c2: num 8
#> $ p2:List of 3
#> ..$ type : chr "B"
#> ..$ score:List of 2
#> .. ..$ c1: num 9
#> .. ..$ c2: num 9
#> ..$ p3 :List of 2
#> .. ..$ type : chr "B"
#> .. ..$ score:List of 2
Created on 2019-02-20 by the reprex package (v0.2.1)
Upvotes: 2
Reputation: 79208
You can write your own function to do this:
check = function(x,name){
m = names(x)%in% name
x = if(any(m)) x[!m] else x
if(is.list(x)) sapply(x,check,name)
else x
}
dput(check(x,'c3'))
list(p1 = list(type = "A", score = list(c1 = 10, c2 = 8)), p2 = list(
type = "B", score = list(c1 = 9, c2 = 9), p3 = list(type = "B",
score = list(c1 = 9, c2 = 7))))
This is also vecotized in that it can discard all that you need. eg try check(x,c('c1','c3'))
Upvotes: 3