rguides

negate

negate() is in the purrr package, not base R. It flips a predicate function — what returned TRUE starts returning FALSE, and vice versa. It’s an adverb in the purrr sense: a function that modifies another function rather than operating on data directly.

Basic Usage

The core pattern is simple: wrap a predicate and get its opposite.

library(purrr)

x <- list(a = 1:5, b = letters, c = seq(1, 10, 2))

# Keep numeric elements
x |> keep(is.numeric) |> names()
# [1] "a" "c"

# Keep non-numeric elements (flip the predicate)
x |> keep(negate(is.numeric)) |> names()
# [1] "b"

negate(is.numeric) is equivalent to discard(is.numeric):

x |> discard(is.numeric) |> names()
# [1] "b"

How negate Works

negate() takes a predicate function and returns a new function that wraps it:

# negate creates a new function
is_non_numeric <- negate(is.numeric)

is_non_numeric(1:5)
# [1] FALSE

is_non_numeric(letters)
# [1] TRUE

The negation applies to the logical output, not the elements:

# All elements are positive? Yes.
all(c(1, 2, 3) > 0)
# [1] TRUE

# negate: flip the result
negate(\(x) all(x > 0))
# function(x) all(x > 0) with logic flipped

Test it directly:

pred <- negate(\(x) all(x > 0))
pred(c(1, 2, 3))
# [1] FALSE

Specifying the Predicate

You can pass predicates in three ways:

Named function:

x |> keep(is.character) |> names()
x |> keep(negate(is.character)) |> names()

Anonymous function with ~ formula (older style):

x |> keep(~ all(.x > 0)) |> names()

Purrr-style anonymous function with \(x):

x |> keep(\(x) length(x) > 3) |> names()
x |> keep(negate(\(x) length(x) > 3)) |> names()

Using negate With Base R Functions

Many base R logical functions work as predicates:

x <- list(x = 1:10, y = rbernoulli(10), z = letters)

# Keep elements that are NOT numeric
x |> keep(negate(is.numeric)) |> names()
# [1] "y" "z"

# Keep elements that do NOT have length > 5
x |> keep(negate(\(x) length(x) > 5)) |> names()

When negate Helps

Making Code Readable

Negating a condition can be clearer than embedding a negation in the predicate:

# A double-negative is harder to parse
x |> keep(\(x) !all(is.na(x)))

# Negate the predicate itself — reads as "not all missing"
x |> keep(negate(\(x) all(is.na(x))))

The second form reads naturally: “keep elements where it is NOT true that all values are missing.”

Composing With Other Predicates

library(purrr)

x <- list(a = 1:10, b = -5:5, c = 50:60)

# Keep elements that have any non-positive values
x |> keep(\(e) any(e <= 0) && length(e) > 3) |> names()
# [1] "b"

# Equivalent — "elements that are not all positive"
x |> keep(\(e) !all(e > 0))

See Also