rguides

endsWith()

endsWith(x, suffix)

The endsWith() function checks whether a character vector ends with a specified substring. It returns a logical vector indicating match status.

Syntax

endsWith(x, suffix)

Parameters

ParameterTypeDefaultDescription
xcharacterRequiredA character vector to check
suffixcharacterRequiredThe suffix to look for at the end of each string

Examples

Basic usage

# Check if strings end with a suffix
files <- c("data.csv", "script.R", "report.csv", "model.rds")

endsWith(files, ".csv")
# [1]  TRUE FALSE  TRUE FALSE

endsWith(files, ".R")
# [1] FALSE  TRUE FALSE FALSE

Case sensitivity

endsWith() performs exact, case-sensitive suffix matching. The string "file.CSV" does not end with ".csv" because the function treats uppercase and lowercase as distinct characters. When checking file extensions from user input or cross-platform sources, normalize both sides with tolower() before calling endsWith() to avoid false negatives from inconsistent capitalization.

# These functions are case-sensitive
endsWith("file.CSV", ".csv")
# [1] FALSE

endsWith("file.csv", ".csv")
# [1] TRUE

Empty suffix

An empty suffix matches every string — endsWith(x, "") always returns TRUE for non-NA inputs because every string technically ends with the empty string. This behavior is mathematically sound but can produce confusing results when suffix is computed programmatically and accidentally resolves to "". Guard against empty computed suffixes in validation code by checking for zero-length input before calling endsWith().

# Empty suffix matches everything
endsWith(c("a", "b", "c"), "")
# [1] TRUE TRUE TRUE

Common patterns

Beyond simple checks, endsWith() shines in practical data-cleaning pipelines. A common pattern combines case normalization with suffix matching to build a file-type validator that works across platforms. The tolower() wrapper ensures users who type .PDF or .Pdf get the same result, while endsWith() keeps the check fast by avoiding regex compilation. This approach handles the majority of real-world file-validation needs without pulling in extra packages.

File extension checking

# Validate file extensions
validate_extension <- function(filename, valid_ext) {
  ext <- paste0(".", valid_ext)
  endsWith(tolower(filename), tolower(ext))
}

validate_extension("document.PDF", "pdf")
# [1] TRUE

validate_extension("image.jpg", "png")
# [1] FALSE

Filtering by file type

Once you can validate individual extensions, the natural next step is bulk filtering — selecting only matching files from a larger list. The boolean vector from endsWith() feeds directly into R’s [ subset operator, making files[endsWith(files, ".csv")] idiomatic for extracting CSV files from a directory listing. This is faster than grep() for fixed suffixes and reads clearly to anyone maintaining the code later.

# Get all CSV files
files <- c("data.csv", "report.csv", "script.R", "model.rds")
files[endsWith(files, ".csv")]
# [1] "data.csv"  "report.csv"

Path processing

endsWith() operates on any character vector, not just bare filenames. Full file paths, URLs, and identifiers with namespace prefixes can all be checked. When working with directory paths, remember that endsWith() matches the full string end — /data.csv will match only if the entire path ends with exactly that substring. For partial-path matching, combine with basename() or dirname() first.

# Check file paths
paths <- c("/home/user/file.txt", "/home/admin/data.csv", "/home/user/image.png")

# Find all files in a directory
endsWith(paths, "/data.csv")
# [1] FALSE  TRUE FALSE

endsWith() vs grepl() and when to use each

endsWith() is faster than grepl("\\.(csv)$", x) for suffix matching because it avoids regex compilation and uses a direct string comparison. Prefer endsWith() for fixed suffixes, file extensions, URL endings, identifier namespaces — and grepl() for patterns that need wildcards or alternation.

endsWith() is case-sensitive. The file extension check pattern in the example above (converting both sides with tolower()) is the right approach for systems where users might type .CSV, .Csv, or .csv interchangeably.

An empty suffix matches every string, and NA inputs return NA in the output. Both behaviors follow the standard R conventions for vectorized string functions.

When you need to match multiple possible suffixes, the cleanest approach is to call endsWith() once per suffix and combine with |: endsWith(x, ".csv") | endsWith(x, ".tsv"). This is cleaner than a regex like grepl("\\.(csv|tsv)$", x) because it avoids regex escaping and is easier to extend with additional file types later. For very large lists of possible suffixes, switch to a regex with alternation as it compiles once rather than running multiple passes.

Like startsWith(), endsWith() was added in R 3.3.0 and is vectorized over both arguments. If both x and suffix are vectors of the same length, comparison is element-wise. If you want to test all elements of x against all possible suffixes, use a loop or sapply over the suffix list and combine the results with rowSums or Reduce("|", ...). The function is straightforward to use in dplyr::filter() pipelines and anywhere a logical predicate is expected.

endsWith() is case-sensitive and accepts only literal suffix strings, not patterns. For case-insensitive suffix matching, use grepl() with $ anchor and ignore.case = TRUE, or normalize case before comparison: endsWith(tolower(x), tolower(suffix)). For testing multiple possible suffixes, grepl("(suffix1|suffix2)$", x) is more concise than multiple endsWith() calls connected with |.

Both startsWith() and endsWith() short-circuit on empty strings: endsWith(x, "") returns TRUE for every non-NA element, since every string ends with the empty string. This property is occasionally useful for generating a vector of all-TRUE values, but it is more often a source of confusion when suffix is computed rather than literal and accidentally evaluates to "".

See also