rubyguides

Array#intersection

array & other_array -> array

The intersection method returns a new array containing elements that appear in all the arrays. When you use the & operator, it performs the same operation between exactly two arrays.

Syntax

array & other_array
array.intersection(*other_arrays)

The two forms serve different use cases. & works with exactly two arrays and reads like a set operator, which keeps inline expressions concise. The method form accepts a variable number of arguments, so you can intersect three or more arrays in a single call without chaining multiple & operations. Both share the same underlying logic: elements must appear in every input array to survive into the result, and duplicates are always removed.

Parameters

ParameterTypeDefaultDescription
other_arrayArrayThe array to intersect with
*other_arraysArrayAdditional arrays (for the method form)

Examples

Basic usage with the & operator

a = [1, 2, 3, 4, 5]
b = [3, 4, 5, 6, 7]

a & b
# => [3, 4, 5]

This first example shows the shortest path through the feature. & is handy when you just need the overlap and do not care about how many times a value appeared in either input. It reads like a set operation, so the intent stays clear even when the array data comes from a larger pipeline. That makes it a good fit for quick filters, small data transforms, and checks that need a simple yes-or-no view of shared values.

Using the method form with multiple arrays

a = [1, 2, 3, 4]
b = [2, 3, 5, 6]
c = [2, 3, 7, 8]

a.intersection(b, c)
# => [2, 3]

The method form helps when more than two arrays need to be compared at once. Instead of chaining several & calls, you can pass every candidate list into a single expression and let Ruby handle the overlap. That keeps the code easier to scan when you are comparing tags, roles, or other lists that all need to agree before you move forward. It also makes the intent of the operation obvious to the next person reading the file.

Handling duplicate values

a = [1, 1, 2, 2, 3]
b = [1, 2, 2, 3, 3]

a & b
# => [1, 2, 3]

The result contains each common element only once, even if it appeared multiple times in the original arrays. That behavior matters when your source data is noisy or when a value can be repeated for reasons that do not affect the final answer. In those cases, intersection acts like a clean summary step, turning two verbose inputs into one compact result that is easier to compare, display, or pass to the next operation.

Common Patterns

Filtering array contents

# Keep only elements that exist in a whitelist
allowed = [1, 2, 3]
user_input = [1, 4, 2, 5, 3, 6]

user_input & allowed
# => [1, 2, 3]

This pattern works well for validation steps because it lets you compare incoming values against a known list without writing a longer loop. If the filtered array is empty, you know nothing matched. If it contains one or more values, you can continue with those accepted items and ignore the rest. That makes the logic easy to explain in code review and easy to adapt when the whitelist changes.

Set operations

# Find common tags between two posts
post1_tags = ["ruby", "rails", "tutorial"]
post2_tags = ["ruby", "rails", "performance"]

common = post1_tags & post2_tags
# => ["ruby", "rails"]

This is the kind of example that makes intersection feel natural in day-to-day Ruby code. Tags, labels, permissions, and feature flags often need to be compared across more than one record, and the shared items are usually the part you want to keep. By keeping the operation focused on overlap, the code stays short and the result is easy to reason about before any later formatting or sorting runs.

when intersections help

intersection is a natural fit when the arrays represent groups, tags, permissions, or any other list where the overlap matters more than the full set of values. It keeps the code focused on what is shared and avoids a manual loop with extra bookkeeping. That makes it easy to read in code that filters down to a common subset before the next step runs. If the result needs to be displayed or sorted, do that separately so the set logic stays simple and direct.

The method also removes duplicates in the result, which is often exactly what you want when the array is acting like a set. That behavior helps keep reports and comparisons tidy because the answer is the common values themselves, not a count of how many times they appeared. When order matters later, add a follow-up step that documents the chosen order instead of trying to make intersection do two jobs at once.

Finding shared permissions

admin_perms = ["read", "write", "delete", "manage_users"]
editor_perms = ["read", "write", "publish"]
viewer_perms = ["read"]

admin_perms.intersection(editor_perms, viewer_perms)
# => ["read"]

Checking shared permissions across roles is a natural fit for intersection because the operation asks a single question: what do all these roles have in common? When the result is empty, you know there is no universal permission. When it contains values, those are the ones every role can safely use.

Comparing configuration keys

required_keys = ["host", "port", "database"]
provided_keys = ["host", "database", "username"]

missing = required_keys - (required_keys & provided_keys)
# => ["port"]

Combining intersection with set difference gives you a quick way to identify gaps without a manual loop over every key. The intersection step isolates the shared subset, and the difference step reveals what the required set expects that the provided set did not include.

See Also