Skip to content

Commit 5177959

Browse files
yutannihilationhadley
authored andcommitted
Add negate argument to str_subset(), str_detect(), and str_which() (#259)
1 parent 567eb4d commit 5177959

File tree

7 files changed

+49
-15
lines changed

7 files changed

+49
-15
lines changed

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
* `str_interp()` now renders lists consistently independent on the presence of
44
additional placeholders (@amhrasmussen)
55

6+
* `str_subset()`, `str_detect()`, and `str_which()` gets `negate` argument,
7+
which is useful when you want the elements that do NOT match (#259,
8+
@yutannihilation).
9+
610
# stringr 1.3.1
711

812
* `str_replace_all()` with a named vector now respects modifier functions (#207)

R/detect.r

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#' Match character, word, line and sentence boundaries with
2121
#' [boundary()]. An empty pattern, "", is equivalent to
2222
#' `boundary("character")`.
23+
#'
24+
#' @param negate If `TRUE`, return non-matching elements.
2325
#' @return A logical vector.
2426
#' @seealso [stringi::stri_detect()] which this function wraps,
2527
#' [str_subset()] for a convenient wrapper around
@@ -35,12 +37,15 @@
3537
#'
3638
#' # Also vectorised over pattern
3739
#' str_detect("aecfg", letters)
38-
str_detect <- function(string, pattern) {
40+
#'
41+
#' # Returns TRUE if the pattern do NOT match
42+
#' str_detect(fruit, "^p", negate = TRUE)
43+
str_detect <- function(string, pattern, negate = FALSE) {
3944
switch(type(pattern),
4045
empty = ,
41-
bound = str_count(string, pattern) > 0,
42-
fixed = stri_detect_fixed(string, pattern, opts_fixed = opts(pattern)),
43-
coll = stri_detect_coll(string, pattern, opts_collator = opts(pattern)),
44-
regex = stri_detect_regex(string, pattern, opts_regex = opts(pattern))
46+
bound = str_count(string, pattern) > 0 & !negate,
47+
fixed = stri_detect_fixed(string, pattern, negate = negate, opts_fixed = opts(pattern)),
48+
coll = stri_detect_coll(string, pattern, negate = negate, opts_collator = opts(pattern)),
49+
regex = stri_detect_regex(string, pattern, negate = negate, opts_regex = opts(pattern))
4550
)
4651
}

R/subset.R

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,24 @@
2323
#' str_subset(fruit, "b")
2424
#' str_subset(fruit, "[aeiou]")
2525
#'
26+
#' # Returns elements that do NOT match
27+
#' str_subset(fruit, "^p", negate = TRUE)
28+
#'
2629
#' # Missings never match
2730
#' str_subset(c("a", NA, "b"), ".")
2831
#' str_which(c("a", NA, "b"), ".")
29-
str_subset <- function(string, pattern) {
32+
str_subset <- function(string, pattern, negate = FALSE) {
3033
switch(type(pattern),
3134
empty = ,
32-
bound = string[str_detect(string, pattern)],
33-
fixed = stri_subset_fixed(string, pattern, omit_na = TRUE, opts_fixed = opts(pattern)),
34-
coll = stri_subset_coll(string, pattern, omit_na = TRUE, opts_collator = opts(pattern)),
35-
regex = stri_subset_regex(string, pattern, omit_na = TRUE, opts_regex = opts(pattern))
35+
bound = string[str_detect(string, pattern) & !negate],
36+
fixed = stri_subset_fixed(string, pattern, omit_na = TRUE, negate = negate, opts_fixed = opts(pattern)),
37+
coll = stri_subset_coll(string, pattern, omit_na = TRUE, negate = negate, opts_collator = opts(pattern)),
38+
regex = stri_subset_regex(string, pattern, omit_na = TRUE, negate = negate, opts_regex = opts(pattern))
3639
)
3740
}
3841

3942
#' @export
4043
#' @rdname str_subset
41-
str_which <- function(string, pattern) {
42-
which(str_detect(string, pattern))
44+
str_which <- function(string, pattern, negate = FALSE) {
45+
which(str_detect(string, pattern, negate = negate))
4346
}

man/str_detect.Rd

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/str_subset.Rd

Lines changed: 7 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-detect.r

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ test_that("special cases are correct", {
88
test_that("vectorised patterns work", {
99
expect_equal(str_detect("ab", c("a", "b", "c")), c(T, T, F))
1010
expect_equal(str_detect(c("ca", "ab"), c("a", "c")), c(T, F))
11+
12+
# negation works
13+
expect_equal(str_detect("ab", c("a", "b", "c"), negate = TRUE), c(F, F, T))
1114
})
1215

1316
test_that("modifiers work", {

tests/testthat/test-subset.r

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,20 @@ test_that("basic subsetting for fixed patterns works", {
66
str_subset(c("i", "I"), fixed("i", ignore_case = TRUE)),
77
c("i", "I")
88
)
9+
10+
# negation works
11+
expect_equal(str_subset(c("i", "I"), fixed("i"), negate = TRUE), "I")
912
})
1013

1114
test_that("str_which is equivalent to grep", {
1215
expect_equal(
1316
str_which(head(letters), "[aeiou]"),
1417
grep("[aeiou]", head(letters))
1518
)
19+
20+
# negation works
21+
expect_equal(
22+
str_which(head(letters), "[aeiou]", negate = TRUE),
23+
grep("[aeiou]", head(letters), invert = TRUE)
24+
)
1625
})

0 commit comments

Comments
 (0)