AlgoInsight

Q&A

What is array.uniq in Ruby?

By AlgoInsight Team2026-06-18arraysruby

Overview

uniq is one of Ruby's most idiomatic array methods. It removes duplicates by comparing elements with eql? and hash - matching Ruby's equality semantics. The block form extends this to deduplicate by any derived value, making it powerful for working with collections of objects.

graph LR
    A["[1,2,2,3,1].uniq"] --> B["[1,2,3] - new array, first-occurrence order"]
    C["array.uniq!"] --> D["Modifies in place - returns nil if no change"]
    E["array.uniq {|x| x.attr}"] --> F["Dedup by block value"]
    style A fill:#1e1b4b,color:#fff

Basic Usage

arr = [1, 2, 2, 3, 1, 4, 3]
arr.uniq         # => [1, 2, 3, 4]   - new array, original unchanged
puts arr.inspect # [1, 2, 2, 3, 1, 4, 3]  - unchanged!

uniq returns a new array; the original is untouched.

In-Place Variant: uniq!

arr = [1, 2, 2, 3]
arr.uniq!         # => [1, 2, 3]  - original modified
puts arr.inspect  # [1, 2, 3]

arr2 = [1, 2, 3]
arr2.uniq!        # => nil  (no duplicates to remove - returns nil, not the array)

uniq! returns nil when no elements are removed - a common gotcha: don't assume the return value is always the array.

Block Form: Custom Uniqueness

Pass a block to define how elements are compared. The first element yielding each distinct block value is kept; subsequent elements yielding the same value are dropped:

# Deduplicate by the first character:
["apple", "avocado", "banana", "blueberry"].uniq { |s| s[0] }
# => ["apple", "banana"]  - "avocado" dropped (a), "blueberry" dropped (b)

# Deduplicate objects by an attribute:
users = [
  {id: 1, name: "Ada"},
  {id: 2, name: "Linus"},
  {id: 1, name: "Ada (duplicate)"},
]
users.uniq { |u| u[:id] }
# => [{id:1, name:"Ada"}, {id:2, name:"Linus"}]  - first occurrence of id:1 kept

This is how you deduplicate an array of complex objects by a key without writing a manual loop.

How Uniqueness Is Determined

Without a block, uniq uses eql? and hash (Ruby's standard equality for hash/set membership):

[1, 1.0].uniq   # => [1, 1.0]  - Integer and Float are not eql?
["a", :a].uniq  # => ["a", :a]  - different types

Strings are compared by value:

["hello", "hello", "world"].uniq   # => ["hello", "world"]

Related Methods

Method Purpose
uniq / uniq! Remove duplicates
uniq { } Remove duplicates by block value
tally (Ruby 2.7+) Count occurrences: ["a","b","a"].tally # => {"a"=>2,"b"=>1}
group_by Group elements by a key
count Count occurrences of a value

With Comparable Objects

For custom objects to be deduplicated correctly without a block, define eql? and hash:

class Point
  attr_reader :x, :y
  def initialize(x, y) = @x, @y = x, y
  def eql?(other) = x == other.x && y == other.y
  def hash = [x, y].hash
end

[Point.new(1,2), Point.new(1,2), Point.new(3,4)].uniq
# => [Point(1,2), Point(3,4)]

Without overriding these, each object is unique by identity (object_id).

Why This Matters in Interviews

Ruby interviews use uniq to test array manipulation and the block-form idiom. Key points: uniq returns a new array (doesn't mutate), uniq! mutates and returns nil when there's nothing to change (not the array), the block form deduplicates by a derived value (the standard way to deduplicate objects by attribute), and the underlying equality is eql?/hash. Showing the block form with objects signals real Ruby fluency beyond toy examples.

Common Pitfalls

References

More answers