Array#uniq

Returns: Array · Updated April 1, 2026 · Array Methods
ruby array uniq deduplicate stdlib

What does uniq do?

uniq returns a new array with duplicate elements removed. By default, elements are compared using == — if two elements are equal, only the first occurrence is kept and subsequent duplicates are discarded.

Array#uniq is defined directly on the Array class but behaves identically to Enumerable#uniq — Array includes Enumerable and overrides uniq with an optimised implementation.

Basic Usage

Removing duplicate numbers

[1, 2, 2, 3, 1, 4, 3, 5].uniq
# => [1, 2, 3, 4, 5]

The first occurrence of each value is preserved. 1 appears at index 0, 2 at index 1, 3 at index 3, etc.

With strings

names = ["Alice", "Bob", "Alice", "Charlie", "Bob", "Diana"]
names.uniq
# => ["Alice", "Bob", "Charlie", "Diana"]

Empty and single-element arrays

[].uniq
# => []

[42].uniq
# => [42]

With a Block

Pass a block to determine uniqueness based on the block’s return value instead of the elements themselves:

words = ["apple", "apricot", "banana", "blueberry", "cherry"]
words.uniq { |word| word[0] }
# => ["apple", "banana", "cherry"]

"apple"[0] and "apricot"[0] both return "a", so only the first "a"-word survives. The block result drives uniqueness — elements producing the same block value are considered duplicates.

Practical example: unique by attribute

users = [
  { name: "Alice", role: "admin" },
  { name: "Bob", role: "member" },
  { name: "Charlie", role: "admin" },
  { name: "Diana", role: "member" }
]

users.uniq { |u| u[:role] }
# => [{:name=>"Alice", :role=>"admin"},
#     {:name=>"Bob", :role=>"member"}]

One record per role — useful when you need a representative from each category.

Order Preservation

uniq preserves the original order of elements. The first occurrence of each unique element is kept in its original position:

[3, 1, 4, 1, 5, 9, 2, 6].uniq
# => [3, 1, 4, 5, 9, 2, 6]

This matters when the sequence of elements carries meaning — unlike sorting or other transformations that reorder results.

The Bang Variant: uniq!

The uniq! method mutates the original array in place. If duplicates were removed, it returns the same array. If no duplicates existed, it returns nil.

numbers = [1, 2, 2, 3, 1, 4, 3, 5]

result = numbers.uniq!

numbers  # => [1, 2, 3, 4, 5]
result   # => [1, 2, 3, 4, 5]
result.equal?(numbers)  # => true  (same object)

When uniq! returns nil

If the array already contains only unique elements, uniq! returns nil and leaves the array unchanged:

numbers = [1, 2, 3]

result = numbers.uniq!
result   # => nil
numbers  # => [1, 2, 3]

Comparing uniq vs uniq!

original = [1, 2, 2, 3, 1]

# uniq returns a new array, original unchanged
deduped = original.uniq
original  # => [1, 2, 2, 3, 1]
deduped   # => [1, 2, 3]

# uniq! modifies the original
original.uniq!
original  # => [1, 2, 3]

Difference from & (Set Intersection)

Ruby has two ways to work with unique elements. uniq removes duplicates from a single array. & finds elements present in both of two arrays:

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

# uniq removes duplicates from ONE array
a.uniq
# => [1, 2, 3, 4]

# & finds elements present in BOTH arrays
a & b
# => [2, 3]

uniq operates on one array and deduplicates it. & requires two arrays and returns their intersection.

When to use each

  • Use uniq when you want to remove duplicates from one list
  • Use & when you want common elements between two lists
# Deduplicate one list
["cat", "dog", "cat", "mouse"].uniq
# => ["cat", "dog", "mouse"]

# Find overlapping tags
user_tags = ["ruby", "rails", "postgres"]
post_tags = ["ruby", "python", "postgres"]
user_tags & post_tags
# => ["ruby", "postgres"]

Performance Considerations

uniq iterates through the array once and uses a hash internally to track seen elements. This gives it O(n) time complexity, where n is the array size.

require "benchmark"

array = (1..1000).to_a + (1..500).to_a  # 1500 elements, 500 duplicates

Benchmark.measure do
  array.uniq
end.real   # => ~0.0002s

The uniq! variant avoids allocating a new array, which saves memory for large arrays. The iteration cost is the same — the performance difference is solely in allocation.

For block-based uniqueness, the hash stores the block result as the key rather than the element itself:

data = [1, 2, 3, 4, 5]
data.uniq { |n| n.even? ? "even" : "odd" }
# => [1, 2]

This is still O(n) — hashing the block result is constant time.

Edge Cases

nil values

nil is treated as a regular value and participates in uniqueness checking:

[1, nil, 2, nil, 3].uniq
# => [1, nil, 2, 3]

One nil is kept alongside one occurrence of each unique non-nil value.

With a block returning nil

If the block returns nil for multiple elements, those elements are considered duplicates:

data = [1, 2, 3, 4]
data.uniq { |n| n.even? ? "even" : nil }
# => [1, 2]

Odd numbers (1, 3) both produce nil from the block, so only the first odd number survives.

Hash elements

Hashes use hash and eql? for uniqueness, not just ==:

[{a: 1}.hash]  # not directly comparable to [{a: 1}]

Ruby’s Hash class handles deduplication internally, but when using uniq on an array of hashes, two hashes with identical contents are considered equal.

Practical Examples

Removing duplicate names

attendees = ["Alice", "Bob", "Charlie", "Alice", "Diana", "Bob"]
attendees.uniq
# => ["Alice", "Bob", "Charlie", "Diana"]

Finding unique values by attribute

products = [
  { name: "Laptop", category: "electronics" },
  { name: "Mouse", category: "electronics" },
  { name: "Carrot", category: "food" },
  { name: "Apple", category: "food" }
]

products.uniq { |p| p[:category] }
# => [{:name=>"Laptop", :category=>"electronics"},
#     {:name=>"Carrot", :category=>"food"}]

Cleaning extracted URLs

urls = [
  "https://example.com/page1",
  "https://example.com/page2",
  "https://example.com/page1",
  "https://example.com/page3"
]

urls.uniq
# => ["https://example.com/page1",
#     "https://example.com/page2",
#     "https://example.com/page3"]

Chaining with other array methods

numbers = [1, 2, 2, 3, 3, 3, 4, 4, 5]

numbers.uniq.select(&:odd?)        # => [1, 3, 5]
numbers.uniq.map { |n| n * 10 }    # => [10, 20, 30, 40, 50]
numbers.uniq.reject { |n| n > 3 }  # => [1, 2, 3]

See Also