rguides

intersect()

intersect(x, y)

The intersect() function returns the elements that appear in both of two vectors. It is useful for comparing IDs, categories, or labels shared between datasets.

Syntax

intersect(x, y)

Parameters

ParameterTypeDescription
xvectorFirst input vector
yvectorSecond input vector

intersect() compares the two inputs and keeps only the values that exist in both. Duplicates are dropped from the result even if they appear multiple times in the original vectors. The return type matches the type of the first argument, so intersect(c(1,2), c(2,3)) returns an integer vector while intersect(c("a","b"), c("b","c")) returns a character vector.

Examples

Basic usage

x <- c(1, 2, 3, 4)
y <- c(3, 4, 5, 6)

intersect(x, y)
# [1] 3 4

The numeric vectors x and y share the values 3 and 4, so those are the only elements returned. Notice that 3 and 4 appear in their original order from xintersect() preserves the ordering of the first argument, not the second. This ordering guarantee is useful when the first vector represents a priority or ranking that you want the common elements to respect.

Character vectors

a <- c("apple", "banana", "cherry")
b <- c("banana", "cherry", "date")

intersect(a, b)
# [1] "banana" "cherry"

String comparison in intersect() is case-sensitive: “Apple” and “apple” are treated as different values. If you need case-insensitive intersection, convert both vectors to the same case with tolower() or toupper() before calling intersect(). The function also handles Unicode characters correctly, so it works with non-ASCII text without any extra configuration.

Finding common iDs

# Users who purchased both products
product_a_buyers <- c("user_1", "user_2", "user_3", "user_4")
product_b_buyers <- c("user_3", "user_4", "user_5", "user_6")

intersect(product_a_buyers, product_b_buyers)
# [1] "user_3" "user_4"

This pattern is common in marketing analytics and user research: you have two lists of IDs from different campaigns or products, and you want to find the overlap. The result tells you which users engaged with both campaigns. For production code with millions of IDs, consider storing the data in a database and using a SQL INTERSECT query instead of loading everything into R memory.

Common patterns

Filtering based on another dataset

# Active users from a larger user list
all_users <- c("user_1", "user_2", "user_3", "user_4", "user_5")
active_users <- c("user_1", "user_3", "user_5")

intersect(all_users, active_users)
# [1] "user_1" "user_3" "user_5"

Filtering one list against another is a two-step mental model: first identify which values are shared, then use those shared values to subset the original data. In base R you would combine intersect() with bracket indexing, but for data frames the tidyverse functions semi_join() and inner_join() handle both steps in one call while preserving all columns.

Common elements from multiple vectors

a <- c(1, 2, 3)
b <- c(2, 3, 4)
c <- c(3, 4, 5)

intersect(intersect(a, b), c)
# [1] 3

intersect() in practice

intersect(x, y) returns elements that appear in both x and y, with duplicates removed. The result is a unique set of common elements. The order of the result follows the order they appear in x.

Unlike SQL-style joins, intersect() does not preserve row structure, it works on flat vectors, not data frames. For data frames, use dplyr::inner_join() or dplyr::semi_join() instead.

intersect() works with any atomic vector type: integers, doubles, characters, and logicals. For character vectors, comparison is case-sensitive. The function is not aware of missing values in a meaningful way, NA is treated as a value and two NAs will intersect.

The order of arguments does not change which elements are returned, but it does affect the order of the output. intersect(a, b) returns elements ordered by their position in a, while intersect(b, a) returns them ordered by their position in b.

Vectorized set operations in R are implemented efficiently and handle large vectors well. For very large sets (millions of elements), consider using data.table::fintersect() which is optimized for performance with data.table objects. For character vectors, intersect() performs exact string matching, use grep() or regex if you need pattern-based intersection.

When two vectors share no common elements, intersect() returns character(0), integer(0), or the appropriate typed empty vector. Always check the length of the result when it drives downstream logic: if (length(intersect(a, b)) > 0) is safer than checking the result directly in a boolean context.

Note that intersect() de-duplicates the output, even if a value appears multiple times in both x and y, it appears only once in the result.

intersect() is one-indexed and position-independent — intersect(c(3,1,2), c(1,2,3)) returns c(3,1,2), preserving the order of the first argument. This is unlike SQL INTERSECT which returns rows in undefined order. When you need a sorted intersection, wrap the result with sort(). Note also that intersect() implicitly calls unique() on both inputs, so duplicate values in either vector are collapsed before the comparison is made.

See also