The Comparable mixin is part of the Ruby core. It’s used by classes that can be compared. It includes a bunch of methods used for object comparison purposes.

For example, the Numeric and String classes include this mixin

irb> Numeric.ancestors

=> [Numeric, Comparable, Object, Kernel, BasicObject]

irb> String.ancestors

=> [String, Comparable, Object, Kernel, BasicObject]

NB: Feel free to take a look at this article if you want to learn more about the Module#ancestors method.

Before to start

I’m thrilled to share with you our latest project: Fun Facts about Ruby — Volume 1

Please feel free to spread the word and share this post! 🙏

Thank you for your time!

Implementing a class that includes Comparable

Let’s say that we want to compare profiles by their rank attribute

class Profile

include Comparable

attr :rank def initialize(rank)

@rank = rank

end def <=>(other_profile)

rank <=> other_profile.rank

end

end irb> profile21 = Profile.new(21)

=> #<Profile:0x0404 @rank=21>

irb> profile42 = Profile.new(42)

=> #<Profile:0x0404 @rank=42>

irb> profile84 = Profile.new(84)

=> #<Profile:0x0404 @rank=84>

irb> profile84 > profile42

=> true

irb> profile84 < profile42

=> false

irb> profile84 == profile84

=> true

irb> profile21 <= profile42

=> true

irb> profile84 >= profile42

=> true

A class that includes the Comparable mixin must respond to the <=> method (also known as the spaceship operator).

This method is used by all the methods implemented in the Comparable mixin ( == , <= , >= , > , < , etc..).

As the rank attribute is an Integer — which is the direct child of the Numeric class — we can use the Numeric implementation of the <=> method to safely compare each rank.

Enumerable#sort

As a class that includes the Comparable mixin must respond to the <=> method, it becomes possible to call the Enumerable#sort method to sort a series of profiles .

In effect, by default the sort method will be processed by using the <=> method of each compared item

irb> profiles = []

=> []

irb> 10.downto(5) { |n| profiles << Profile.new(rand(1..n)) }

=> 10

irb> profiles

=> [

#<Profile:0x00007fda9a079be0 @rank=8>,

#<Profile:0x00007fda9a079af0 @rank=6>,

#<Profile:0x00007fda9a079a00 @rank=3>,

#<Profile:0x00007fda9a079910 @rank=1>,

#<Profile:0x00007fda9a079820 @rank=2>,

#<Profile:0x00007fda9a079708 @rank=4>

]

irb> profiles.sort

=> [

#<Profile:0x00007fda9a079be0 @rank=1>,

#<Profile:0x00007fda9a079af0 @rank=2>,

#<Profile:0x00007fda9a079a00 @rank=3>,

#<Profile:0x00007fda9a079910 @rank=4>,

#<Profile:0x00007fda9a079820 @rank=6>,

#<Profile:0x00007fda9a079708 @rank=8>

]

Comparable#between

I would like to introduce you to the Comparable#between? method. This method allows you to know if an object is included within a range

irb> 12.between?(0, 24)

=> true

irb> 'aaa'.between?('bbb', 'ccc')

=> false

As we’ve included the Comparable mixin in our Profile class then we can use the between? method. Notice that the between? comparison is inclusive

irb> profile21 = Profile.new(21)

=> #<Profile:0x0404 @rank=21>

irb> profile42 = Profile.new(42)

=> #<Profile:0x0405 @rank=42>

irb> profile84 = Profile.new(84)

=> #<Profile:0x0406 @rank=84>

irb> profile42.between?(profile21, profile84)

=> true

irb> profile21.between?(profile21, profile84)

=> true

irb> profile84.between?(profile21, profile84)

=> true

Voilà !

May I have your attention please 🎤🎤

Feel free to subscribe here: www.rubycademy.com