The Comparable Module
The Comparable module is one of Ruby’s most useful built-in modules. It lets you compare objects using familiar operators like <, >, and ==. In this guide, you’ll learn how to include Comparable in your classes and define the spaceship operator to enable powerful comparison functionality.
Why Use Comparable?
When you include the Comparable module in a class and define the <=> (spaceship) operator, Ruby automatically gives you all these methods for free:
<(less than)<=(less than or equal)>(greater than)>=(greater than or equal)==(equal)between?(range check)
This is incredibly useful for sorting, finding minimum/maximum values, and any scenario where you need to compare objects.
Including Comparable in Your Class
Here’s a simple example using a Player class for a game:
class Player
include Comparable
attr_reader :name, :score
def initialize(name, score)
@name = name
@score = score
end
def <=>(other)
score <=> other.score
end
def to_s
"#{name}: #{score}"
end
end
The key is defining the <=> method, which returns:
-1when self < other0when self == other1when self > othernilwhen comparison isn’t possible
Using the Comparison Methods
Once Comparable is included, you get all the comparison operators:
player1 = Player.new("Alice", 1500)
player2 = Player.new("Bob", 2000)
player3 = Player.new("Charlie", 1500)
player1 < player2 # => true
player2 > player1 # => true
player1 <= player3 # => true (same score)
player1 == player3 # => true (same score)
player1 >= player2 # => false
player2.between?(player1, player3) # => false
Practical Examples
Sorting Players
players = [
Player.new("Alice", 1500),
Player.new("Bob", 2000),
Player.new("Charlie", 1800)
]
players.sort.each { |p| puts p }
# Output:
# Alice: 1500
# Charlie: 1800
# Bob: 2000
Finding Min/Max
players = [
Player.new("Alice", 1500),
Player.new("Bob", 2000),
Player.new("Charlie", 1800)
]
players.min # => #<Player:0x00007f8a2c3d4 @name="Alice", @score=1500>
players.max # => #<Player:0x00007f8a2c3e5 @name="Bob", @score=2000>
Working with Numbers
Ruby’s core classes already include Comparable. Here are some examples:
# Strings are compared lexicographically
"apple" < "banana" # => true
"zebra" > "apple" # => true
# Numbers work as expected
5.between?(1, 10) # => true
3.5.between?(3, 4) # => false
# Arrays are compared element by element
[1, 2, 3] < [1, 2, 4] # => true
[1, 2, 3] == [1, 2, 3] # => true
Defining Multiple Comparison Criteria
You can chain comparisons for more complex sorting:
class Team
include Comparable
attr_reader :name, :wins, :losses
def initialize(name, wins, losses)
@name = name
@wins = wins
@losses = losses
end
def <=>(other)
# First compare by wins, then by losses (fewer is better)
result = other.wins <=> wins
result.zero? ? losses <=> other.losses : result
end
end
teams = [
Team.new("Lions", 10, 4),
Team.new("Tigers", 10, 3),
Team.new("Bears", 8, 6)
]
teams.sort.each { |t| puts "#{t.name}: #{t.wins}-#{t.losses}" }
# Output:
# Tigers: 10-3
# Lions: 10-4
# Bears: 8-6
Common Mistakes
Forgetting to Include Comparable
# Without Comparable - won't work!
class BadExample
def initialize(value)
@value = value
end
def <=>(other)
@value <=> other.value
end
end
a = BadExample.new(5)
b = BadExample.new(10)
a < b # => NoMethodError: undefined method `<'
Returning nil Incorrectly
Always return -1, 0, or 1 for valid comparisons:
def <=>(other)
return nil if other.nil? # Handle nil case explicitly
@value <=> other.value
end
Summary
The Comparable module is a powerful way to add comparison functionality to your classes:
- Include Comparable in your class
- Define the
<=>method returning -1, 0, or 1 - Get free comparison methods:
<,<=>,>,>=,between?
This pattern is used throughout Ruby’s standard library. The String, Numeric, Array, and Hash classes all include Comparable. Now you can make your own classes work just as well with sorting, min/max operations, and comparison operators.