rguides

match()

match(x, table, nomatch = NA_integer_, incomparables = FALSE)

match() returns the position of the first match between elements of x in table.

Syntax

match(x, table, nomatch = NA_integer_, incomparables = FALSE)

Parameters

ParameterTypeDefaultDescription
xvector,The values to look up
tablevector,The values to match against
nomatchintegerNA_integer_Value to return when no match is found
incomparablesvectorFALSEValues in x that cannot be matched

Examples

Basic usage

x <- c("a", "b", "c", "d")
table <- c("b", "c", "e")

match(x, table)
# [1] NA  1  2 NA

The basic call shows match() comparing each element of x against table and returning the position where it first appears. Elements not found in table receive NA — the default for nomatch. This is the foundation for all lookup-based operations with match().

Using nomatch parameter

match(c("apple", "cherry"), c("apple", "banana"), nomatch = 0)
# [1] 1 0

Setting nomatch = 0 replaces NA with a numeric sentinel, which avoids NA propagation in downstream arithmetic. This is especially useful when match() output feeds directly into computations where NA would cascade. In practice, a sentinel can also be a negative number or any value that is safely distinguishable from valid match positions.

Finding unique or duplicate values

new_values <- c("apple", "banana", "cherry", "apple")
seen <- c("apple", "banana")

# Get indices of matches
idx <- match(new_values, seen)
idx
# [1]  1  2 NA  1

This example demonstrates detecting which new values have been seen before — the NA at position 3 tells you “cherry” is new, while the 1 at position 4 confirms the second “apple” maps to the first position in seen. This pattern underlies deduplication and incremental processing workflows where you maintain a running set of known identifiers.

Practical data cleaning

df <- data.frame(
  category = c("A", "B", "C", "D", "A"),
  value = 1:5
)

allowed <- c("A", "B")
idx <- match(df$category, allowed)
idx
# [1]  1  2 NA NA  1

Common patterns

Deduplication: Identify which values in one vector appear in another. By combining match() with is.na(), you can separate seen values from new ones — a pattern that appears in data validation, incremental file processing, and any workflow where you maintain a master list and need to check incoming values against it.

existing_ids <- c(101, 102, 103)
new_ids <- c(101, 104, 105, 103)

# Which new IDs already exist?
!is.na(match(existing_ids, new_ids))
# [1]  TRUE FALSE FALSE

# Get only the new IDs
setdiff(new_ids, existing_ids)
# [1] 104 105

match() in practice

match(x, table) returns the position of the first match for each element of x in table. When an element of x is not found in table, the result is NA. This makes match() a lookup function: table[match(x, names)] is equivalent to a dictionary lookup, returning the value at the position where x matches the key.

%in% is built on match(): x %in% table is equivalent to !is.na(match(x, table)). For membership testing alone, %in% is more readable. Use match() when you need the actual position, not just whether a match exists.

A common pattern: aligning two vectors by a shared key. match(df1$id, df2$id) returns the row in df2 that corresponds to each row in df1. df2[match(df1$id, df2$id), ] reorders df2 to align with df1, a manual join. For this operation at scale, merge() or dplyr::left_join() is more efficient and handles duplicates and NA more explicitly.

match() finds the first match only. For finding all matches, use which(table == x[i]) in a loop or lapply(x, function(v) which(table == v)). For large tables, converting to a hash via setNames(seq_along(table), table) and using named list access is faster than repeated match() calls.

match() returns the position of each element of x in table, or NA if not found. It returns only the first match for each element. x %in% table is the logical version, it returns TRUE/FALSE rather than positions. For joining data frames, match() is the engine behind merge: df1[match(df2$id, df1$id), ] is a manual left-join. When table has unique values, match() is faster than which() for repeated lookups.

match() returns the position of each element of x in table, returning NA where no match is found. For checking membership without needing positions, x %in% table is cleaner, it returns a logical vector and is implemented as !is.na(match(x, table)).

match() returns the first match when table contains duplicates. If you need all matching positions, which(table == value) gives every position. For vectorized lookup, match() is efficient because it builds a hash table internally — it is faster than a loop for large x or table.

A common pattern is using match() to reorder or subset: df[match(key_vector, df$id), ] returns rows of df in the order specified by key_vector, with NA rows for unmatched keys. This is the basis of manual left-join operations in base R.

pmatch() allows partial string matching. charmatch() is similar but stricter. Both are useful for matching abbreviated user input against a table of valid values in function argument handling.

See also