ComparisonJust like other object oriented languages, Ruby gives an object ways to find out if it is equal to, greater or less than another object. Object comparison is extremely important, not only do we tend to often explicitly compare objects to each other e.g.:

ruby string1 = "abc" if "abc" == string1 puts 'they are equal' end

but objects are frequently compared and tested for equality ‘behind the scenes’, by core and library classes (i.e. ordering of objects in collections etc.). So, lets not waste any time and jump straight in.

Testing Objects For Equality

Ruby has three main equality test methods, ==, eql? and equal?. These methods normally live in the Object class and since all other Ruby classes inherit from Object, they automatically gain access to these three methods. Inside the Object class all there methods do exactly the same thing, they test if two objects are exactly the same object. That is to say, both objects must have the same object id. We can easily demonstrate this e.g.:

```ruby string1 = “abc” class MyObject end object1 = MyObject.new object2 = object1 object3 = MyObject.new

puts “Object 1 is == to object 2: #{object1 == object2}” puts “Object 1 is eql? to object 2: #{object1.eql? object2}” puts “Object 1 is equal? to object 2: #{object1.equal? object2}” puts “Object 1 is == to object 3: #{object1 == object3}” puts “Object 1 is eql? to object 3: #{object1.eql? object3}” puts “Object 1 is equal? to object 3: #{object1.equal? object3}”```

The output is:

Object 1 is == to object 2: true
Object 1 is eql? to object 2: true
Object 1 is equal? to object 2: true
Object 1 is == to object 3: false
Object 1 is eql? to object 3: false
Object 1 is equal? to object 3: false

As you can see, when two variables are referencing the same object, calling any of the three equality methods will tell us that the two object are equal. As soon as two variables reference different objects (even if everything about the objects is otherwise identical), all three methods will tell us that the objects are unequal.

Of course, it is not very useful to have 3 different methods that do the same thing. Usually with Ruby we will tend to leave the equal? method alone to always give us the ability to find out if two objects are exactly the same. The eql? and == methods however are open to be redefined in any way we like. It might still seem a little strange to have two methods that can ostensibly do the same thing. I will come back to this point a little later. In the meantime lets look at the == method (or operator if you prefer).

You can redefine the == method to give your objects custom behavior when it comes to equality testing. Lets do this for a Sock class:

```ruby class Sock attr_reader :size def initialize size @size = size end

def ==(another_sock) self.size == another_sock.size end end```

You may now compare your Sock objects in an intuitive fashion based on the size. More than that, by defining the == method on your object, you also get the != method for free which allows you to test your objects for inequality, very handy e.g.:

```ruby sock1 = Sock.new(10) sock2 = Sock.new(11) sock3 = Sock.new(10)

puts “Are sock1 and sock2 equal? #{sock1 == sock2}” puts “Are sock1 and sock3 equal? #{sock1 == sock3}” puts “Are sock1 and sock2 NOT equal? #{sock1 != sock2}”```

Which produces:

Are sock1 and sock2 equal? false
Are sock1 and sock3 equal? true
Are sock1 and sock2 NOT equal? true

But, what if you want to compare two objects to find out which one is greater?

Comparing Ruby Objects

The == is not only an equality method, it is also part of a family of comparison methods that also include, >, <, >=, <=, and !=. Whenever you need to be able to compare your object and not just test for equality, redefining the == method is no longer enough and you must take a different approach.

In order to give your object the ability to be compared to other objects, you need to do two things:

  • Mix in the the Ruby Comparable module into your class
  • Define a method called <=>, this is known as the comparison method or ‘spaceship method’

Mixing in the module is pretty simple, but how do you define the <=> method? This is also fairly intuitive, if you’re familiar with any other object oriented language. If you’ve ever used the Comparable interface in Java for example, you’ll be right at home. Essentially, the method must return –1 when you think your current object is smaller than the one you’re comparing against. If you think it is larger you need to return +1, otherwise you return zero (the objects are equal). Let’s have a look at an example:

```ruby class Sock include Comparable attr_reader :size def initialize size @size = size end

def <=>(another_sock) if self.size < another_sock.size -1 elsif self.size > another_sock.size 1 else 0 end end end```

Defining the spaceship method allows your object to use the whole suite of comparison methods (==, >, <, >=, <=, and !=) e.g.:

```ruby sock1 = Sock.new(10) sock2 = Sock.new(11) sock3 = Sock.new(10)

puts “Are sock1 and sock3 equal? #{sock1 == sock3}” puts “Are sock1 and sock2 NOT equal? #{sock1 != sock2}” puts “Is sock1 > sock3? #{sock1 > sock3}” puts “Is sock1 < sock2? #{sock1 < sock2}” puts “Is sock1 >= sock3? #{sock1 >= sock3}” puts “Is sock1 <= sock2? #{sock1 <= sock2}”```

Which produces:

Are sock1 and sock3 equal? true
Are sock1 and sock2 NOT equal? true
Is sock1 > sock3? false
Is sock1 < sock2? true
Is sock1 >= sock3? true
Is sock1 <= sock2? true

If you ask me, this is a pretty decent amount of functionality to get out of defining just one method. It is interesting to note that you can easily define all those comparison methods separately, instead of defining the spaceship, if you were so inclined.

Also, notice that you automatically get the == method by defining the spaceship, so you don’t need to provide separate equality behavior for your object any more. Although the Ruby built-in String class does define the == method separately from that provided by the <=> method, so you’re still able to customize the behavior of comparison methods even if you’ve already defined the <=> method.

Eql? vs ==

Equality

I did say I would come back to this one :). As I mentioned in core Ruby classes the equal? method is used to to find out if two objects have the same object id (are the same object). The == is normally used to find out if two objects have the same value (as you would expect). The third method, _eql? is normally used to test if two object have the same value as well as the same type_. For example:

ruby puts "Does integer == to float: #{25 == 25.0}" puts "Does integer eql? to float: #{25.eql? 25.0}"

This gives:

Does integer == to float: true
Does integer eql? to float: false

You are of course welcome to reproduce similar behavior in your classes when you define your equality methods. However it does seem a little redundant to me. If you need two object to be equal then you will probably want them to be the same type in the first place (the above example with integers and floats is the only exception I can think of), in which case you would make sure they were the same type even in the == method. If you don’t really care about type and just care about the behavior of the objects (which is entirely possible considering Ruby’s dynamic nature, with duck typing and all), then you probably wouldn’t test for type equality in either the == method or the eql? method. Basically the eql? becomes redundant.

There is only one scenario that I can see where the eql? method might come in handy. If you have to defined the <=> method, therefore giving your object an implicit == method, it may no longer make sense to redefine == separately (although as I mentioned above, nothing stops you from doing so). But if for some reason you need to keep the <=> equality behavior but still need even more custom equality behavior, you can always use the eql? method for this purpose. I can’t really envisage a situation where you might want to do this, but the capability is certainly there.

This leaves the eql? method as a rather useless one. From what I can see, it is defined and used by many of the core Ruby classes as well as the library classes so you need to be aware of it and know when it is likely to come into play. However I can’t really see you ever needing to define it for your own classes given that you have equal? and == already. I would welcome some discussion on this one. If someone known of a good reason to have the eql? method around then I would love to hear about it. As it stands, this is all you really need to know about equality and object comparison in Ruby. More Ruby discussion is coming up, so don’t forget to subscribe to my feed if you don’t want to miss it.

Images by The Artifex and rstrawser