Checking Equality of Objects in Ruby

There are four methods for checking equality of objects in the Ruby programming language:

  • === used for case comparison
  • == used for equality of objects
  • eql? used for determining object identity
  • equal used for comparing the hash key of two objects

Case Equality with ===

The === method is used for checking the case equality of two objects. When you use the case statement, this method will be the one used to see whether or not there is a match.

Here's an example. We have a class Book where we are storing a unique id, the isbn. When two books are compared, they are equal if their ISBNs are the same.

class Book
  attr_accessor :isbn
  
  def initialize(isbn)
    @isbn = isbn
  end

  def ===(other)
    isbn == other.isbn
  end
end

bookA = Book.new(1000)
bookB = Book.new(2000)

puts bookA === bookA # => true
puts bookA === Book.new(1000) # => false
puts bookB === bookA # => false
puts bookB === bookB # => true
puts bookB === Book.new(1000) # => false
puts bookB === Book.new(2000) # => true

puts case Book.new(2000)
  when bookA
    'ISBN matches book A'
  when bookB
    'ISBN matches book b'
  else
    'no match'
end # => ISBN matche book b

If we did not override the === equality method, the default comparison would have used the == method.

Let's consider another example: a user session for product analytics. The class has a field for the user id and the session start time. We can define the equality to be a comparison between both fields. Since only comparing the user ids would result in all sessions from one user to be seen as equivalent, both fields must be compared.

class UserSession
  attr_accessor :user_id, :session_start_time
  
  def ==(other)
    self.user_id == other.user_id && self.session_start_time == other.session_start_time
  end
end

== equality comparison

The == method is typically overridden by classes to provide class-specific meaning to the equality comparison.

This is similar to what we did above in the Book code example:

class Book
  attr_accessor :title
  
  def initialize(title)
    @title = title
  end

  def ==(other)
    title.downcase == other.title.downcase
  end
end

eql? comparison

The eql? method compares the object hash key.

For example:

value = {'a': 1}

other = {'a': 1}
puts value == other # => true
puts value.eql?(other) # => true

other = value
puts value == other # => true
puts value.eql?(other) # => true

equal? comparison

In contrast to eql?, the equal? method compares the object identity.

For example:

value = {'a': 1}

other = {'a': 1}
puts value == other # => true
puts value.equal?(other) # => false

other = value
puts value == other # => true
puts value.equal?(other) # => true