rubyguides

Array#difference

What does Array#difference do?

Array#difference returns a new array containing the elements that are present in self but not in the given arrays. It performs a set-style comparison — each element is checked for presence in the argument arrays, and all matching occurrences are excluded.

That gives you a compact way to subtract one list from another without writing a manual loop. The method stays readable even when you pass more than one array to remove against.

Basic usage

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

a.difference(b)
# => [1, 3, 5]

The result is a fresh array that includes only the elements from a that did not appear in b. That is the central idea behind difference: it expresses set subtraction as a method call, so the code says exactly what it does without extra bookkeeping. Because it returns a new object, the source stays safe for any later code that still needs the full list.

The original array a is not modified:

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

a.difference(b)
# => [1, 3, 5]
a
# => [1, 2, 3, 4, 5]

Showing the original array after the call confirms that difference never touches the source. That immutability is a deliberate design choice: the method borrows the data, computes the subtraction, and hands back a new result. If you later need to update the same variable, assign the return value or reach for a different approach entirely.

Removing all occurrences

Note that difference removes all occurrences of each element found in the argument arrays:

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

a.difference(b)
# => [1, 3, 4]

Every 2 in a is removed, not just one.

That all-occurrences behavior is what makes difference different from a simple one-for-one removal. It is closer to set subtraction than to index-based deletion.

Difference with multiple arrays

You can pass multiple arrays to difference. It removes elements present in any of them:

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

a.difference(b, c)
# => [1, 3, 6]

Passing several arrays is useful when the exclusion list is spread across different sources. Ruby collects the removals into one call, so the result still reads like a single subtraction step.

difference vs the - operator

Array#difference and Array#- are functionally identical. - is the traditional operator:

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

a - b
# => [1, 3, 5]

a.difference(b)
# => [1, 3, 5]

Use whichever you prefer. - reads more naturally in expressions; #difference reads as a method call and can be chained.

That trade-off mostly comes down to style and context. In a longer chain, the method form can be easier to scan because it stays consistent with the rest of the call.

difference vs delete_if

delete_if removes elements based on a condition, while difference removes based on set membership:

# delete_if removes conditionally
a = [1, 2, 3, 4, 5]
a.delete_if { |x| x.even? }
# => [1, 3, 5]

# difference removes by set comparison
a = [1, 2, 3, 4, 5]
b = [2, 4]
a.difference(b)
# => [1, 3, 5]

delete_if evaluates a block for each element. difference checks membership in another array without a block.

That difference matters when the rule you want is simply “remove these known values.” In that case, difference keeps the code shorter and more direct.

Practical examples

Removing excluded categories

available = ["ruby", "python", "javascript", "go", "rust"]
excluded = ["python", "go"]

available.difference(excluded)
# => ["ruby", "javascript", "rust"]

Filtering an allowlist of categories is a common pattern in configuration code where you have a master list and need to subtract a blocklist. The output is straightforward: whatever was in the first list but not in the second survives. This pattern scales well because you can keep adding excluded values without changing the filtering logic.

Filtering out blocked users

all_users = ["alice", "bob", "charlie", "diana", "eve"]
blocked = ["bob", "diana"]

all_users.difference(blocked)
# => ["alice", "charlie", "eve"]

User filtering is another natural fit because the blocked list is typically maintained separately from the active user list. Calling difference keeps the two concerns independent: you update the blocklist in one place and the active set adjusts automatically at the point where you need it.

Removing admin roles from a permission list

all_roles = ["read", "write", "delete", "admin", "superadmin"]
elevated = ["admin", "superadmin"]

all_roles.difference(elevated)
# => ["read", "write", "delete"]

The method is handy for permission lists, tag cleanup, and other exclusion tasks where the values are already known. The result is easy to pass to the next step because it stays an array.

Performance notes

Array#difference has a time complexity of O(n), where n is the length of self. It builds a set from the argument arrays for O(1) lookup, then iterates through self once.

For large arrays or frequent calls, consider whether the argument arrays are large — if both arrays are large, the operation can become expensive. In such cases, a Set may offer better performance depending on your use case.

That final trade-off is worth keeping in mind if the same exclusions are reused often. The best choice depends on whether you care more about one-off readability or repeated lookups.

See Also