rguides

purrr::keep

keep(), discard(), and compact() are predicate functionals in purrr. They filter a list or vector based on a condition you provide.

keep() — Retain Matching Elements

keep() calls your predicate function on each element and keeps only the ones where the predicate returns TRUE:

library(purrr)

x <- list(a = 1:3, b = "hello", c = 4:6, d = "world")

keep(x, is.numeric)
#> $a
#> [1] 1 2 3
#>
#> $c
#> [1] 4 5 6

discard() — Drop Matching Elements

discard() is the inverse — it keeps elements where the predicate returns FALSE:

discard(x, is.numeric)
#> $b
#> [1] "hello"
#>
#> $d
#> [1] "world"

compact() — Remove Empty Elements

compact() removes elements that are NULL or have length zero:

y <- list(a = "foo", b = NULL, c = integer(0), d = NA, e = list())

compact(y)
#> $a
#> [1] "foo"
#>
#> $d
#> [1] NA

compact() with a predicate keeps only elements where the predicate returns TRUE for a non-empty, non-NULL value.

Predicate Syntax

The .p argument accepts several forms:

Named function:

keep(x, is.character)

Anonymous function (lambda):

keep(x, \(elem) length(elem) > 1)

String shorthand for lists — selects elements where the named sub-element is truthy:

z <- list(
  list(name = "alice", active = TRUE),
  list(name = "bob", active = FALSE),
  list(name = "carol", active = TRUE)
)

keep(z, "active")
#> [[1]]
#> $name
#> [1] "alice"
#>
#> [[2]]
#> $name
#> [1] "carol"

Additional Arguments

Pass extra arguments to your predicate with ...:

nums <- list(a = c(1, 2, 3), b = c(4, 5), c = c(6, 7, 8, 9))

# Keep elements where mean is greater than 4
keep(nums, \(x) mean(x) > 4)
#> $b
#> [1] 4 5
#>
#> $c
#> [1] 6 7 8 9

Pipe-Friendly Chaining

Because the data comes first, these work well in pipes:

library(dplyr)

mtcars %>%
  split(.$cyl) %>%
  keep(\(df) nrow(df) > 10) %>%
  map(summary)

Relationship to Base R

keep() is similar to base::Filter() but with more convenient argument order and stricter predicate checking. In Python, these are like filter() and reject() from itertools.

See Also