R/detect.R

Defines functions str_ends str_starts str_detect

Documented in str_detect str_ends str_starts

#' Detect the presence or absence of a pattern in a string
#'
#' @description Vectorised over `string` and `pattern`, though using vectorised
#' patterns is relatively slow compared to `stringr`.
#' Equivalent to `grepl(pattern, x)`.
#' See `str_which()` for an equivalent to `grep(pattern, x)`.
#'
#' @param string `character vector` of strings.
#' @param pattern `string` or `character vector`, pattern(s) to match. Can be:
#'   * A Perl-compatible regular expression (default).
#'   * Wrap with `perl(ignore_case = TRUE)` to use case-insensitive matching.
#'   * Wrap with `fixed()` to use a fixed/literal match.
#'   * Wrap with `regex()` to use a POSIX 1003.2 extended regular expression.
#'   * Wrap with `regex(ignore_case = TRUE)` to use case-insensitive matching with
#'     a POSIX 1003.2 extended regular expression.
#' @param negate `logical`, if `TRUE` return non-matching elements. Default is
#' `FALSE`.
#'
#' @return Returns a `logical vector` the same length as `string`.
#'
#' @examples
#' fruit <- c("apple", "banana", "pear", "pineapple")
#' str_detect(fruit, "a")
#' str_detect(fruit, "^a")
#' str_detect(fruit, "a$")
#' str_detect(fruit, "b")
#' str_detect(fruit, "[aeiou]")
#'
#' # Also vectorised over pattern
#' str_detect("aecfg", letters)
#'
#' # Returns TRUE if the pattern do NOT match
#' str_detect(fruit, "^p", negate = TRUE)
#'
#' @export
str_detect <- function(string, pattern, negate = FALSE) {
  check_lengths(string, pattern)

  if (length(pattern) > 1) {
    out <- mapply(
      function(p, s) {
        grepl(p, s,
              fixed = is_fixed(p),
              perl = is_perl(p),
              ignore.case = ignore_case(p))
      },
      pattern,
      string,
      SIMPLIFY = TRUE,
      USE.NAMES = FALSE
    )
  } else {
    out <- grepl(pattern, string,
                 fixed = is_fixed(pattern),
                 perl = is_perl(pattern),
                 ignore.case = ignore_case(pattern))
  }

  out[is.na(string)] <- NA

  if (negate) {
    !out
  } else {
    out
  }
}

#' Detect the presence or absence of a pattern at the beginning or end of a
#' string.
#'
#' @description Vectorised over `string` and `pattern`, though using vectorised
#' patterns is relatively slow compared to `stringr`.
#'
#' @param string `character vector` of strings.
#' @param pattern `character`, a pattern with which the string should start
#' or end.
#'
#' Can be:
#'   * A Perl-compatible regular expression (default).
#'   * Wrap with `perl(ignore_case = TRUE)` to use case-insensitive matching.
#'   * Wrap with `fixed()` to use a fixed/literal match.
#'   * Wrap with `regex()` to use a POSIX 1003.2 extended regular expression.
#'   * Wrap with `regex(ignore_case = TRUE)` to use case-insensitive matching with
#'     a POSIX 1003.2 extended regular expression.
#' @param negate `logical`, if `TRUE` return non-matching elements. Default is
#' `FALSE`.
#'
#' @return Returns a `logical vector` the same length as `string`.
#' @seealso [str_detect()] which this function wraps.
#' @examples
#' fruit <- c("apple", "banana", "pear", "pineapple")
#' str_starts(fruit, "p")
#' str_starts(fruit, "p", negate = TRUE)
#' str_ends(fruit, "e")
#' str_ends(fruit, "e", negate = TRUE)
#'
#' @export
str_starts <- function(string, pattern, negate = FALSE) {
  check_lengths(string, pattern)

  pattern2 <- paste0("^(", pattern, ")")
  attributes(pattern2) <- attributes(pattern)
  str_detect(string = string, pattern = pattern2, negate = negate)
}

#' @rdname str_starts
#' @export
str_ends <- function(string, pattern, negate = FALSE) {
  check_lengths(string, pattern)

  pattern2 <- paste0("(", pattern, ")$")
  attributes(pattern2) <- attributes(pattern)
  str_detect(string = string, pattern = pattern2, negate = negate)
}
csdaw/stringrb documentation built on Aug. 13, 2022, 10:55 p.m.