Brandon Rice

Software development with a focus on web technologies.

Ruby Comparable Mixin

| Comments

It’s often desirable to compare two or more objects for some sort of custom sort order.  Ruby implements this by default in many classes using the comparison (<=>) operator.  This comparison operator is used internally whenever you call the sort method to impose some kind of order on an array of strings, integers, or anything else.  Comparing two arbitrary objects directly using the comparison operator will return -1, 0, or 1 depending on if the calling object is less than, equal to, or greater than the object it’s being compared to.

For example…

1
2
1.9.3p286 :001 > ["charlie", "bravo", "alpha"].sort => ["alpha", "bravo", "charlie"]
1.9.3p286 :002 > 5 <=> 2 => 1
If you have a unique class that needs comparison behavior (or alternatively you want to override the default comparison behavior in some existing class), it’s as easy as including the Ruby Comparable mixin.  If you’re familiar with the Java Comparable interface, then this is very similar.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person
  include Comparable

  attr_accessor :title

  def initialize(title)
    @title = title
  end

  def <=>(other)
    return -1 if @title == 'boss'
    @title <=> other.title
  end
end
After including the Comparable mixin, simply define a <=> method that implements the comparison behavior you’re looking for.  In this case, I always want my “boss” sorted to the top of the list in any comparison.  Otherwise, just perform a default comparison between titles of the two Person objects. That would be a string comparison in this scenario, meaning an alphabetical sort.

1
2
3
4
5
6
7
8
9
10
11
1.9.3p286 :002 > a = Person.new('boss')
 => #<Person:0x000000022661f8 @title="boss">
1.9.3p286 :003 > b = Person.new('worker')
 => #<Person:0x00000002258670 @title="worker">
1.9.3p286 :004 > c = Person.new('customer')
 => #<Person:0x000000022a01f0 @title="customer">
1.9.3p286 :005 > [b, c, a].sort
 => [#<Person:0x000000022661f8 @title="boss">, #<Person:0x000000022a01f0 @title="customer">, #<Person:0x00000002258670 @title="worker">]
1.9.3p286 :006 > b <=> c
 => 1
1.9.3p286 :007 >
Defining the <=> method will also give you all of the default comparison operators (<, <=, ==, >=, and >), as well as the between? method. It should be noted that it’s generally a good idea to have your comparison return nil in the event that it can’t be compared to the other object.