Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

str_ilike() #544

Merged
merged 15 commits into from
Aug 20, 2024
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ Config/testthat/edition: 3
Encoding: UTF-8
LazyData: true
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.2.3
RoxygenNote: 7.3.1
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export(str_flatten)
export(str_flatten_comma)
export(str_glue)
export(str_glue_data)
export(str_ilike)
export(str_interp)
export(str_length)
export(str_like)
Expand Down
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# stringr (development version)

* Introduce `str_ilike()` that follows the conventions of the SQL ILIKE
operator. The `ignore_case` argument from `str_like()` is deprecated, with
`str_like()` now always case sensitive to better follow the conventions of
the SQL LIKE operator (@edward-burn, #543).

# stringr 1.5.1

* Some minor documentation improvements.
Expand Down
53 changes: 46 additions & 7 deletions R/detect.R
Original file line number Diff line number Diff line change
Expand Up @@ -119,30 +119,69 @@ str_ends <- function(string, pattern, negate = FALSE) {
#' * `_` matches a single character (like `.`).
#' * `%` matches any number of characters (like `.*`).
#' * `\%` and `\_` match literal `%` and `_`.
#' * The match is case insensitive by default.
#' * The match is case sensitive.
#'
#' @inheritParams str_detect
#' @param pattern A character vector containing a SQL "like" pattern.
#' See above for details.
#' @param ignore_case Ignore case of matches? Defaults to `TRUE` to match
#' the SQL `LIKE` operator.
#' @param ignore_case `r lifecycle::badge("deprecated")`
#' @return A logical vector the same length as `string`.
#' @export
#' @examples
#' fruit <- c("apple", "banana", "pear", "pineapple")
#' str_like(fruit, "app")
#' str_like(fruit, "app%")
#' str_like(fruit, "APP%")
#' str_like(fruit, "ba_ana")
#' str_like(fruit, "%APPLE")
str_like <- function(string, pattern, ignore_case = TRUE) {
#' str_like(fruit, "%apple")
str_like <- function(string, pattern, ignore_case = deprecated()) {
check_lengths(string, pattern)
check_character(pattern)
if (inherits(pattern, "stringr_pattern")) {
cli::cli_abort("{.arg pattern} must be a plain string, not a stringr modifier.")
}
if (lifecycle::is_present(ignore_case)) {
lifecycle::deprecate_warn(when = "1.5.2",
what = "str_like(ignore_case)",
details = "str_like() is always case sensitive. Use
str_ilike() for case insensitive string matching.")
}

pattern <- regex(like_to_regex(pattern), ignore_case = FALSE)
stri_detect_regex(string, pattern, opts_regex = opts(pattern))
}

#' Detect a pattern in the same way as `SQL`'s `ILIKE` operator
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could combine the docs for these two functions. It'd also be useful to add a note about the incorrect behaviour of older versions of str_like()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hadley by this did you mean to use @inheritParams str_like? If so, I've added this here
1a366e3 But please let me know if I've misunderstood as I wasn't sure of what you meant

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant using @rdname; see the changes I just pushed.

#'
#' @description
#' `str_ilike()` follows the conventions of the SQL `ILIKE` operator:
#'
#' * Must match the entire string.
#' * `_` matches a single character (like `.`).
#' * `%` matches any number of characters (like `.*`).
#' * `\%` and `\_` match literal `%` and `_`.
#' * The match is case insensitive.
#'
#' @inheritParams str_detect
#' @param pattern A character vector containing a SQL "like" pattern.
#' See above for details.
#' @return A logical vector the same length as `string`.
#' @export
#' @examples
#' fruit <- c("apple", "banana", "pear", "pineapple")
#' str_ilike(fruit, "app")
#' str_ilike(fruit, "app%")
#' str_ilike(fruit, "APP%")
#' str_ilike(fruit, "ba_ana")
#' str_ilike(fruit, "%apple")
str_ilike <- function(string, pattern) {
check_lengths(string, pattern)
check_character(pattern)
if (inherits(pattern, "stringr_pattern")) {
cli::cli_abort("{.arg pattern} must be a plain string, not a stringr modifier.")
}
check_bool(ignore_case)

pattern <- regex(like_to_regex(pattern), ignore_case = ignore_case)
pattern <- regex(like_to_regex(pattern), ignore_case = TRUE)
stri_detect_regex(string, pattern, opts_regex = opts(pattern))
}

Expand Down
1 change: 1 addition & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ reference:
- invert_match
- str_conv
- str_like
- str_ilike
- str_replace_na
- str_view
- word
Expand Down
36 changes: 36 additions & 0 deletions man/str_ilike.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions man/str_like.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions tests/testthat/_snaps/detect.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,22 @@
Error in `str_like()`:
! `pattern` must be a plain string, not a stringr modifier.

# ignore_case is deprecated

Code
str_like("abc", "ab%", ignore_case = TRUE)
Condition
Warning:
The `ignore_case` argument of `str_like()` is deprecated as of stringr 1.5.2.
i str_like() is always case sensitive. Use str_ilike() for case insensitive string matching.
Output
[1] TRUE

# str_ilike works

Code
str_ilike("abc", regex("x"))
Condition
Error in `str_ilike()`:
! `pattern` must be a plain string, not a stringr modifier.

11 changes: 11 additions & 0 deletions tests/testthat/test-detect.R
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,20 @@ test_that("functions use tidyverse recycling rules", {

test_that("str_like works", {
expect_true(str_like("abc", "ab%"))
expect_false(str_like("abc", "AB%"))
expect_snapshot(str_like("abc", regex("x")), error = TRUE)
})

test_that("ignore_case is deprecated", {
expect_snapshot(str_like("abc", "ab%", ignore_case = TRUE))
})

test_that("str_ilike works", {
expect_true(str_ilike("abc", "ab%"))
expect_true(str_ilike("abc", "AB%"))
expect_snapshot(str_ilike("abc", regex("x")), error = TRUE)
})

test_that("like_to_regex generates expected regexps",{
expect_equal(like_to_regex("ab%"), "^ab.*$")
expect_equal(like_to_regex("ab_"), "^ab.$")
Expand Down
Loading