42

How can I subset a list based on a condition (TRUE, FALSE) in another list? Please, see my example below:

l <- list(a=c(1,2,3), b=c(4,5,6,5), c=c(3,4,5,6))
l
$a
[1] 1 2 3

$b
[1] 4 5 6 5

$c
[1] 3 4 5 6

cond <- lapply(l, function(x) length(x) > 3)
cond
$a
[1] FALSE

$b
[1] TRUE

$c
[1] TRUE

> l[cond]

Error in l[cond] : invalid subscript type 'list'

jazzurro
  • 23,179
  • 35
  • 66
  • 76
jrara
  • 16,239
  • 33
  • 89
  • 120

8 Answers8

40

This is what the Filter function was made for:

Filter(function(x) length(x) > 3, l)
$b
[1] 4 5 6 5

$c
[1] 3 4 5 6
Jason Morgan
  • 2,260
  • 21
  • 24
29

Another way is to use sapply instead of lapply.

cond <- sapply(l, function(x) length(x) > 3)
l[cond]
PatrickR
  • 301
  • 1
  • 2
  • 4
22

[ is expecting a vector, so use unlist on cond:

l[unlist(cond)]
$b
[1] 4 5 6 5

$c
[1] 3 4 5 6
James
  • 65,548
  • 14
  • 155
  • 193
5
> l[as.logical(cond)]
$b
[1] 4 5 6 5

$c
[1] 3 4 5 6
NPE
  • 486,780
  • 108
  • 951
  • 1,012
4

I recently learned lengths(), which gets the length of each element of a list. This allows us to avoid making another list including logical values as the OP tried.

lengths(l)
#a b c 
#3 4 4 

Using this in a logical condition, we can subset list elements in l.

l[lengths(l) > 3]

$b
[1] 4 5 6 5

$c
[1] 3 4 5 6
jazzurro
  • 23,179
  • 35
  • 66
  • 76
1

Well im am very new to R but as it is a functional language by far the best solution according to the previous answers is something like:

filter <- function (inputList, selector) sapply(inputList, function (element) selector(element))

Assume you have a complex list like yours:

myList <- list(
    a=c(1,2,3), 
    b=c(4,5,6,5), 
    c=c(3,4,5,6))

Then you can filter the elements like:

selection <- myList[filter(myList, function (element) length(element) > 3]

Well of course this also works for list that just contain a value at the first level:

anotherList <- list(1, 2, 3, 4)
selection <- myList[filter(anotherList, function (element) element == 2)]

Or you can put it all together like:

filter <- function (inputList, selector) inputList[sapply(inputList, function (element) selector(element))]
0
cond <- lapply(l, length) > 3
l[cond]
Andre Elrico
  • 10,956
  • 6
  • 50
  • 69
0
l <- list(a=c(1,2,3), b=c(4,5,6,5), c=c(3,4,5,6))

l[lengths(l) > 3]

$b
[1] 4 5 6 5

$c
[1] 3 4 5 6

If a condition on value is needed:

cond <- lapply(l, function(i) i > 3)

res <- Map(`[`, l, cond)

res

$a
numeric(0)

$b
[1] 4 5 6 5

$c
[1] 4 5 6

Dharman
  • 30,962
  • 25
  • 85
  • 135