Steve Lane
Steve Lane

Reputation: 769

Best way to make object hash in Ruby?

So I have a class wherein I need to override the equality operators. This is not so hard. But the custom equality operators aren't used unless my_obj.hash is equal for two objects being compared. so we need to override hash()

I'm kind of stuck on the best way to do this. My object embeds three other object instances. I see by example that for simple instance vars you can just take a hash of the vars themselves:

[var1, var2, var3].hash

More specifically, my class has instance vars for three embedded objects, let's call them:

A B1 B2

Two instances of my object are equal if

object1.B1 == object2.B1 && object1.B2 == object2.B2 || 
      object1.B1 == object2.B2 && object1.B2 == object2.B1

In other words, the collection of B1 and B2 has the same two objects in it, regardless of which specific vars they're assigned to.

B1 and B2 have a custom equality mechanism as well.

I'm just not clear on the best strategy for overriding hash() here.

Sorry if the example is abstract, I'm trying to avoid posting a lot of code.

Upvotes: 2

Views: 611

Answers (3)

David Grayson
David Grayson

Reputation: 87396

Try using a Set instead of an array so the order doesn't matter. You have to have this line at the top:

require 'set'

Then make a Set containing both objects and use it to help implement the equality operator and hash method. I assume Set#hash behaves correctly and your can use it in your hash method. Set#== can be used to simplify your equality operator.

http://www.ruby-doc.org/stdlib-2.1.4/libdoc/set/rdoc/Set.html

Upvotes: 2

user513951
user513951

Reputation: 13612

Are your B1 and B2 objects sortable? If so, here's an easy implementation of your hash method:

class MyClass
  def hash
    return [self.B1, self.B2].sort.hash
  end
end

If they aren't currently sortable and it makes no sense to sort them by any inherent value, you could always just sort by object_id:

class BClass
  include Comparable

  def <=> (other)
    case other
    when BClass then return (self.object_id <=> other.object_id)
    else return nil
    end
  end
end

This enables your B1 and B2 objects to sort themselves versus each other, while throwing "ArgumentError: comparison of X with Y failed" versus instances of any other class.

If you're going the route of using object_id, though, it may just be easier to implement your hash method using that to begin with:

class MyClass
  def hash
    return [self.B1.object_id, self.B2.object_id].sort.hash
  end
end

but this will mean only self-same objects will correctly turn up as equal, and not just objects that "look" alike. To understand what I mean, compare the following:

# Comparison of look-alike objects
"a".object_id == "a".object_id  # => false

# Comparison of self-same objects
a = "a"
a.object_id == a.object_id      # => true

Upvotes: 1

Masa Sakano
Masa Sakano

Reputation: 2267

I assume the hash value can be any object as long as it is unique in each object in your case. If that assumption is correct, how about defining the object hash() method returns as an array, for example?

I am not 100% clear what you are trying to achieve. But I have interpreted the order of self.B1 and self.B2 does not matter. Then this is a possibility:

def hash
  [self.B1.hash, self.B2.hash].sort
end

Then you can compare hash() of two objects with

(my_obj1.hash == my_obj2.hash)

Upvotes: -1

Related Questions