Forum: Ruby Overriding eql? and hash

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
4b7968d41ad6bbb107921b44db2b17c3?d=identicon&s=25 César Díaz (cesardiaz)
on 2009-04-26 12:05
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:
nil>

t1.name = "Gracias por elegirme"

t2 = Track.new

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

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.
7a561ec0875fcbbe3066ea8fe288ec77?d=identicon&s=25 Sebastian Hungerecker (Guest)
on 2009-04-26 13:40
(Received via mailing list)
César Díaz wrote:
>     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
4b7968d41ad6bbb107921b44db2b17c3?d=identicon&s=25 César Díaz (cesardiaz)
on 2009-04-26 16:53
It works perfectly.

Thank you very much!!!
This topic is locked and can not be replied to.