-
Notifications
You must be signed in to change notification settings - Fork 292
Expand file tree
/
Copy pathkeep.R
More file actions
93 lines (88 loc) · 2.86 KB
/
keep.R
File metadata and controls
93 lines (88 loc) · 2.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#' Keep/discard elements based on their values
#'
#' `keep()` selects all elements where `.p` evaluates to `TRUE`;
#' `discard()` selects all elements where `.p` evaluates to `FALSE`.
#' `compact()` discards elements where `.p` evaluates to an empty vector.
#'
#' In other languages, `keep()` and `discard()` are often called `select()`/
#' `filter()` and `reject()`/ `drop()`, but those names are already taken
#' in R. `keep()` is similar to [Filter()], but the argument order is more
#' convenient, and the evaluation of the predicate function `.p` is stricter.
#'
#' @param .x A list or vector.
#' @param .p A predicate function (i.e. a function that returns either `TRUE`
#' or `FALSE`) specified in one of the following ways:
#'
#' * A named function, e.g. `is.character`.
#' * An anonymous function, e.g. `\(x) all(x < 0)` or `function(x) all(x < 0)`.
#' * A formula, e.g. `~ all(.x < 0)`. Use `.x` to refer to the first argument.
#' No longer recommended.
#'
#' @seealso [keep_at()]/[discard_at()] to keep/discard elements by name.
#' @param ... Additional arguments passed on to `.p`.
#' @export
#' @examples
#' rep(10, 10) |>
#' map(sample, 5) |>
#' keep(function(x) mean(x) > 6)
#'
#' # Or use shorthand form
#' rep(10, 10) |>
#' map(sample, 5) |>
#' keep(\(x) mean(x) > 6)
#'
#' # Using a string instead of a function will select all list elements
#' # where that subelement is TRUE
#' x <- rerun(5, a = rbernoulli(1), b = sample(10))
#' x
#' x |> keep("a")
#' x |> discard("a")
#'
#' # compact() discards elements that are NULL or that have length zero
#' list(a = "a", b = NULL, c = integer(0), d = NA, e = list()) |>
#' compact()
keep <- function(.x, .p, ...) {
where <- where_if(.x, .p, ...)
.x[!is.na(where) & where]
}
#' @export
#' @rdname keep
discard <- function(.x, .p, ...) {
where <- where_if(.x, .p, ...)
.x[is.na(where) | !where]
}
#' @export
#' @rdname keep
compact <- function(.x, .p = identity) {
.f <- as_mapper(.p)
discard(.x, function(x) is_empty(.f(x)))
}
#' Keep/discard elements based on their name/position
#'
#' @description
#' `keep_at()` and `discard_at()` are similar to `[` or `dplyr::select()`: they
#' return the same type of data structure as the input, but only containing
#' the requested elements. (If you're looking for a function similar to
#' `[[` see [pluck()]/[chuck()]).
#'
#' @seealso [keep()]/[discard()] to keep/discard elements by value.
#' @inheritParams map_at
#' @export
#' @examples
#' x <- c(a = 1, b = 2, cat = 10, dog = 15, elephant = 5, e = 10)
#' x |> keep_at(letters)
#' x |> discard_at(letters)
#'
#' # Can also use a function
#' x |> keep_at(\(x) nchar(x) == 3)
#' x |> discard_at(\(x) nchar(x) == 3)
keep_at <- function(x, at) {
where <- where_at(x, at, user_env = caller_env())
x[where]
}
#' @export
#' @rdname keep_at
discard_at <- function(x, at) {
where <- where_at(x, at, user_env = caller_env())
x[!where]
}