jrara
jrara

Reputation: 17021

Subset elements in a list based on a logical condition

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'

Upvotes: 45

Views: 64284

Answers (8)

user7295844
user7295844

Reputation:

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))]

Upvotes: 1

Dragos Bandur
Dragos Bandur

Reputation: 301

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

Upvotes: 1

jazzurro
jazzurro

Reputation: 23574

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

Upvotes: 4

Andre Elrico
Andre Elrico

Reputation: 11500

cond <- lapply(l, length) > 3
l[cond]

Upvotes: 0

Jason Morgan
Jason Morgan

Reputation: 2330

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

Upvotes: 45

PatrickR
PatrickR

Reputation: 311

Another way is to use sapply instead of lapply.

cond <- sapply(l, function(x) length(x) > 3)
l[cond]

Upvotes: 30

NPE
NPE

Reputation: 500933

> l[as.logical(cond)]
$b
[1] 4 5 6 5

$c
[1] 3 4 5 6

Upvotes: 5

James
James

Reputation: 66874

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

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

$c
[1] 3 4 5 6

Upvotes: 22

Related Questions