Comparable / <=> / ==
How to add comparison and sorting capabilities to your own class using the Comparable module and the spaceship operator <=>.
Syntax
class ClassName
include Comparable # Include the Comparable module.
# Define the <=> operator (the only required implementation).
def <=>(other)
# When self is smaller: return a negative number.
# When equal: return 0.
# When self is larger: return a positive number.
# When not comparable: return nil.
self_value <=> other.comparable_value
end
end
# Once <=> is defined, the following methods become available automatically.
object < other # less than
object <= other # less than or equal to
object > other # greater than
object >= other # greater than or equal to
object.between?(min, max) # checks if within range
object.clamp(min, max) # clamps to range (Ruby 2.4+)
array.sort # sorts an array of objects that implement Comparable.
Method List
| Operator / Method | Description |
|---|---|
| <=> (other) | The spaceship operator. Returns a negative integer if self is less than other, 0 if equal, or a positive integer if greater. |
| < | Returns true when the result of <=> is negative. Generated automatically by Comparable. |
| <= | Returns true when the result of <=> is negative or zero. |
| > | Returns true when the result of <=> is positive. |
| >= | Returns true when the result of <=> is positive or zero. |
| between?(min, max) | Returns true if the value is between min and max, inclusive. |
| clamp(min, max) | Returns the value clamped to the range min..max. |
Sample Code
# Implement Comparable in a Temperature class.
class Temperature
include Comparable
attr_reader :degrees
def initialize(degrees)
@degrees = degrees
end
# Define <=> (this alone enables all comparison operators).
def <=>(other)
@degrees <=> other.degrees
end
def to_s
"#{@degrees}°C"
end
end
freezing = Temperature.new(0)
body_temp = Temperature.new(36.5)
boiling = Temperature.new(100)
fever = Temperature.new(38.5)
# Comparison operators are available.
puts freezing < boiling # true
puts boiling > body_temp # true
puts freezing <= freezing # true
# Range check with between?.
puts fever.between?(body_temp, boiling) # true (above body temp, below boiling)
# Sort with sort.
temperatures = [boiling, freezing, fever, body_temp]
puts temperatures.sort.map(&:to_s).inspect
# ["0°C", "36.5°C", "38.5°C", "100°C"]
# Clamp to a range with clamp.
outside_temp = Temperature.new(45)
clamped = outside_temp.clamp(Temperature.new(10), Temperature.new(40))
puts clamped # 40°C (clamped to the maximum value)
Overview
By including the Comparable module and defining a single <=> (spaceship operator) method, all six comparison operators (<, <=, >, >=, between?, clamp) become available automatically. This is a classic example of how Ruby modules and mixins are used in practice.
<=> must return nil when comparison is not possible. For example, comparing two numbers is straightforward, but when comparing objects of different types, add a type check and return nil if the comparison cannot be performed.
If <=> returns nil, the sort method raises an ArgumentError. For any class that may be used in sorting, always ensure the method returns an integer or 0 in every case.
For details on modules and mixins, see module / include / extend. For class inheritance, see Inheritance / super.
If you find any errors or copyright issues, please contact us.