The Comparable Module
Part of the Object Oriented Programming section of Coddy's Ruby journey. Lesson 29 of 57.
Ruby's built-in module Comparable gives a class the operators <, <=, ==, >, >= and the methods between? and clamp. It needs one method from the class: <=>, which returns a negative number, 0 or a positive number when the object is smaller, equal or larger:
class Weight
include Comparable
attr_reader :grams
def initialize(grams)
@grams = grams
end
def <=>(other)
grams <=> other.grams
end
end
a = Weight.new(500)
b = Weight.new(1200)
puts a < b
puts a == Weight.new(500)
puts b.between?(a, Weight.new(2000))Output:
true
true
trueNumbers and strings already have <=>, so a class usually delegates to one of its values. Comparing arrays works element by element, which sorts by several fields at once:
class Person
include Comparable
attr_reader :last, :first
def initialize(first, last)
@first = first
@last = last
end
def <=>(other)
[last, first] <=> [other.last, other.first]
end
end
a = Person.new("Ada", "Lovelace")
b = Person.new("Alan", "Turing")
c = Person.new("Byron", "Lovelace")
puts a < c
puts b > cOutput:
true
truesort, min and max use <=> too, so objects of the class can be sorted without a block. clamp keeps a value between two others:
class Weight
include Comparable
attr_reader :grams
def initialize(grams)
@grams = grams
end
def <=>(other)
grams <=> other.grams
end
def to_s
"#{grams}g"
end
end
list = [Weight.new(900), Weight.new(150), Weight.new(400)]
puts list.sort.join(", ")
puts list.max
puts Weight.new(5000).clamp(Weight.new(0), Weight.new(1000))Output:
150g, 400g, 900g
900g
1000g<=> should return nil when the other object cannot be compared. Comparable then raises ArgumentError for < and returns false for ==, instead of failing with a confusing NoMethodError:
def <=>(other)
return nil unless other.is_a?(Weight)
grams <=> other.grams
end
Weight.new(1) == 5 # false
Weight.new(1) < 5 # ArgumentError: comparison of Weight with 5 failedChallenge
EasyComplete the class Weight. Weight.parse(text) reads 500g, 1.2kg or 2lb (1 lb is 453.6 g) and stores the grams. Include Comparable and define <=> on the grams. to_s returns the grams rounded to a whole number, like 1200g.
The supplied code parses every input line, then prints the weights sorted, the heaviest, the lightest, whether the first weight is between 1 kg and 2 kg, and the first weight clamped to at most 1 kg.
Your code goes in weight.rb. main.rb holds the supplied input/output code and cannot be edited.
Try it yourself
require_relative 'weight'
# Supplied input/output code: keep it as it is
input = []
while (line = gets)
input << line.chomp
end
weights = input.map { |line| Weight.parse(line) }
puts "sorted: #{weights.sort.join(", ")}"
puts "heaviest: #{weights.max}"
puts "lightest: #{weights.min}"
puts "first between 1kg and 2kg: #{weights[0].between?(Weight.parse("1kg"), Weight.parse("2kg"))}"
puts "first clamped: #{weights[0].clamp(Weight.parse("0g"), Weight.parse("1kg"))}"
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Classes and Objects
Working With FilesDefining ClassesInstance VariablesMethods and selfto_s and inspectRecap - Library Card7Polymorphism and Duck Typing
PolymorphismDuck TypingAbstract Methodscase and ClassesRecap - Payment Methods2Accessors and Self
Readers and WritersCustom Settersself in MethodsIdentity and EqualityRecap - Contact List8Special Methods
Equality and hashOperator MethodsIndexing and AppendingCustom CollectionsRecap - Fractions11Project: Library Management
Books and MembersBorrowing Books3Class Methods and Variables
Class MethodsClass Instance VariablesConstantsFactory MethodsRecap - Coupon Codes6Modules and Mixins
Modules and includeExtending ObjectsThe Comparable ModuleThe Enumerable ModuleRecap - Sports LeaguePractice on your own: Online Ruby compiler