Overriding eql? and hash

Hi,

I am working in an application and I need to store my own objects in
some collections. I was using instances of Array to store that objects,
but now, I need not to store duplicated instances. Searching the Web I
found de class 'Set', which seems to do that. I also read that with this
collection you have to override the eql? and hash methods in your
classes objects to set a criteria to compare you objects.

I will try to explain better myself with an example:

I have the following class Track

class Track < ActiveRecord::Base
  belongs_to :album

  def hash
    StringUtils.nice_slug(self.name, "false").hash

  end

  def eql?(other)
    equal = false
    if StringUtils.nice_slug(self.name, "false").downcase.eql?
StringUtils.nice_slug(other.name, "false").downcase
      equal = true
    end
    equal
  end
end

Now, I create two instances of Track and two of Set, and I insert one
track into each Set.

a = Set::new

t1 = Track.new

=> #<Track id: nil, name: nil, lyric: nil, created_at: nil, updated_at:

t1.name = "Gracias por elegirme"

t2 = Track.new

=> #<Track id: nil, name: nil, lyric: nil, created_at: nil, updated_at:

t2.name = "Gracias Por Elegirme"

a << t1

=> #<Set: {#<Track id: nil, name: "Gracias por elegirme", lyric: nil,
created_at: nil, updated_at: nil>}>

b = Set::new

b << t2

#<Set: {#<Track id: nil, name: "Gracias Por Elegirme", lyric: nil,
created_at: nil, updated_at: nil>}>

And finally, I try to insert the content of b into a:

a.merge(b)

The result that I am expecting is that nothing is inserted into a
because the name of the two tracks is very similar, and when we convert
into downcase the are equal ("gracias por elegirme").

But instead I get the following:

#<Set: {#<Track id: nil, name: "Gracias Por Elegirme", lyric: nil,
created_at: nil, updated_at: nil>, #<Track id: nil, name: "Gracias por
elegirme", lyric: nil, created_at: nil, updated_at: nil>}>

Now a stores the two Track instances.

I would appreciate so much your help.

···

--
Posted via http://www.ruby-forum.com/\.

César Díaz wrote:

def hash
StringUtils.nice_slug(self.name, "false").hash

end

def eql?(other)
equal = false
if StringUtils.nice_slug(self.name, "false").downcase.eql?
StringUtils.nice_slug(other.name, "false").downcase
equal = true
end
equal
end

If two strings are equal except for case your eql? method will return true. Or
at least it would if it ever got called. But since you don't call downcase in
the hash method, those two objects will have different hash values and Set
will never bother to call eql? on them.
In other words: call downcase in the hash method as well and you'll be fine.

HTH,
Sebastian

It works perfectly.

Thank you very much!!!

···

--
Posted via http://www.ruby-forum.com/.