qed
qed

Reputation: 23104

Extract non null elements from a list

I have a list like this:

    x = list(a = 1:4, b = 3:10, c = NULL)
    x
    #$a
    #[1] 1 2 3 4
    #
    #$b
    #[1]  3  4  5  6  7  8  9 10
    #
    #$c
    #NULL

and I want to extract all elements that are not null. How can this be done? Thanks.

Upvotes: 46

Views: 26124

Answers (6)

maike
maike

Reputation: 465

Using purrr's compact() just do

library(purrr)
x = list(a = 1:4, b = 3:10, c = NULL)
compact(x)
#> $a
#> [1] 1 2 3 4
#> 
#> $b
#> [1]  3  4  5  6  7  8  9 10

Created on 2024-09-02 with reprex v2.1.1

Upvotes: 1

GKi
GKi

Reputation: 39647

An option is to use %in%.

x[!x %in% list(NULL)]
#$a
#[1] 1 2 3 4
#
#$b
#[1]  3  4  5  6  7  8  9 10

Or is.null in vapply.

x[!vapply(x, is.null, FALSE)]

Or to use lengths, but this will fail in case the list contains e.g. numeric(0).

x[lengths(x) > 0]

Benchmark

x = list(a = 1:4, b = 3:10, c = NULL)
bench::mark(
sapply = x[!sapply(x,is.null)],
Filter = Filter(Negate(is.null), x),
"in" = x[!x %in% list(NULL)],
lapply = x[!unlist(lapply(x, is.null))],
vapply = x[!vapply(x, is.null, FALSE)],
lengths = x[lengths(x) > 0] )
#  expression     min  median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time
#  <bch:expr> <bch:t> <bch:t>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm>
#1 sapply     19.85µs 22.73µs    40853.        0B    12.3   9997     3    244.7ms
#2 Filter     11.84µs 13.73µs    70067.        0B    14.0   9998     2    142.7ms
#3 in          9.87µs 11.45µs    81415.        0B     8.14  9999     1    122.8ms
#4 lapply       6.4µs  7.49µs   126673.        0B    12.7   9999     1     78.9ms
#5 vapply      4.64µs  5.51µs   177842.        0B    17.8   9999     1     56.2ms
#6 lengths     2.12µs  2.32µs   414271.        0B     0    10000     0     24.1ms

Upvotes: 2

ColinTea
ColinTea

Reputation: 1058

x[!sapply(x,is.null)]

This generalizes to any logical statement about the list, just sub in the logic for "is.null".

Upvotes: 15

Sander
Sander

Reputation: 343

Simpler and likely quicker than the above, the following works for lists of any non-recursive (in the sense of is.recursive) values:

example_1_LST <- list(NULL, a=1.0, b=Matrix::Matrix(), c=NULL, d=4L)
example_2_LST <- as.list(unlist(example_1_LST, recursive=FALSE))

str(example_2_LST) prints:

List of 3
 $ a: num 1
 $ b:Formal class 'lsyMatrix' [package "Matrix"] with 5 slots
  .. ..@ x       : logi NA
  .. ..@ Dim     : int [1:2] 1 1
  .. ..@ Dimnames:List of 2
  .. .. ..$ : NULL
  .. .. ..$ : NULL
  .. ..@ uplo    : chr "U"
  .. ..@ factors : list()
 $ d: int 4

Upvotes: 1

Matthew Plourde
Matthew Plourde

Reputation: 44614

Here's another option:

Filter(Negate(is.null), x)

Upvotes: 54

csgillespie
csgillespie

Reputation: 60452

What about:

x[!unlist(lapply(x, is.null))]

Here is a brief description of what is going on.

  1. lapply tells us which elements are NULL

    R> lapply(x, is.null)
    $a
    [1] FALSE
    
    $b
    [1] FALSE
    
    $c
    [1] TRUE
    
  2. Next we convect the list into a vector:

    R> unlist(lapply(x, is.null)) 
    a     b     c 
    FALSE FALSE  TRUE 
    
  3. Then we switch TRUE to FALSE:

    R> !unlist(lapply(x, is.null))
        a     b     c 
    TRUE  TRUE FALSE 
    
  4. Finally, we select the elements using the usual notation:

    x[!unlist(lapply(x, is.null))]
    

Upvotes: 14

Related Questions