In R, how to filter lists of lists?

Viewed 14037

According to the manual, Filter works on vectors, and it happens to work also on lists, eg.:

z <- list(a=1, b=2, c=3)
Filter(function(i){
  z[[i]] > 1
}, z)
$b
[1] 2

$c
[1] 3

However, it doesn't work on lists of lists, eg.:

z <- list(z1=list(a=1,b=2,c=3), z2=list(a=1,b=1,c=1), z3=list())
Filter(function(i){
  if(length(z[[i]])>0){
    if(z[[i]]$b > 1)
      TRUE
    else
      FALSE
  }
  else
    FALSE
}, z)
Error in z[[i]] : invalid subscript type 'list'

What is the best way then to filter lists of lists without using nested loops? It could also be lists of lists of lists...

(I tried with nested lapply's instead, but couldn't manage to make it work.)

Edit: in the 2nd example, here is what I want to obtain:

list(z1=list(a=1,b=2,c=3))

that is, without z$z2 because z$z2$b < 1, and without z$z3 because it is empty.

5 Answers

The modern tidy solution to this problem would be:

library(tidyverse)
z <- list(z1=list(a=1,b=2,c=3), z2=list(a=1,b=1,c=1), z3=list())

Then simply:

tibble(disc = z, Names = names(z)) %>% 
  hoist(disc, c = "c") %>%
  filter(c == 3) %>%
  unnest_wider(disc) %>% 
  split(.$Names) %>% map(select, -Names) %>% 
  map(as.list)

Note this is now super flexible, and easily allows other filtering, e.g. if a == 1

Filter sub list by key. Written in reading the answers which help me.

zall<-list(z1=list(list(key=1,b=2,c=3),list(key=2,b=3,c=4)))
zall
#> $z1
#> $z1[[1]]
#> $z1[[1]]$key
#> [1] 1
#> 
#> $z1[[1]]$b
#> [1] 2
#> 
#> $z1[[1]]$c
#> [1] 3
#> 
#> 
#> $z1[[2]]
#> $z1[[2]]$key
#> [1] 2
#> 
#> $z1[[2]]$b
#> [1] 3
#> 
#> $z1[[2]]$c
#> [1] 4
lapply(zall$z1, function(x){ x[intersect(names(x),"key")]  } )
#> [[1]]
#> [[1]]$key
#> [1] 1
#> 
#> 
#> [[2]]
#> [[2]]$key
#> [1] 2
lapply(zall$z1, function(x){ x[setdiff(names(x),"key")]  } )
#> [[1]]
#> [[1]]$b
#> [1] 2
#> 
#> [[1]]$c
#> [1] 3
#> 
#> 
#> [[2]]
#> [[2]]$b
#> [1] 3
#> 
#> [[2]]$c
#> [1] 4
Related