Frage

I have a set in Ruby which would hold the objects of a class I created. I am using the set to prevent duplicate values, but I am still getting duplicate values in the set. Am assuming it's because the objects in the sets, even though they have same values they have different address. Below is what I have:

sample_set = Set.new

Present Output of sample_set:

{
    #<SampleModule: : SampleClass: 0x007fba92923ae8@name="Hello World!",
    @id=123>,
    #<SampleModule: : SampleClass: 0x007fba92922fd0@name="Hello World!",
    @id=123>,
    #<SampleModule: : SampleClass: 0x007fba92922418@name="Star Wars",
    @id=456>,
    #<SampleModule: : SampleClass: 0x007fba929217c0@name="Star Wars",
    @id=456>,
    #<SampleModule: : SampleClass: 0x007fba92920ac8@name="Star Wars",
    @id=456>
} 

Expected output of sample_set:

{
    #<SampleModule: : SampleClass: 0x007fba92923ae8@name="Hello World!",
    @id=123>,
    #<SampleModule: : SampleClass: 0x007fba929217c0@name="Star Wars",
    @id=456>
}

Is it possible to achieve this in ruby? Do we need to override 'eql' method to do this?

Solution:

def eql?(other)
  other.instance_of?(self.class) && @name == other.name && @id == other.id
end

def hash
  @name.hash ^ @id.hash
end
War es hilfreich?

Lösung

(Moved from comments above)

You are probably not overriding #hash and #eql? in your SampleClass. Which you need to do in order for data structures like Hashes and Sets to work properly.

Programming languages have a notion of equality of objects that usually consists of some sort of equality function and a hash function (eql? and hash in Ruby). The language uses the equality function to determine if two Objects are equal. The hash function computes a hash (unique value) for each object that is used by certain data structures such as sets and hash tables. The important piece is that if two Objects are equal (in terms of eql?) they must have the same hash value, otherwise the hash tables and sets will not function properly.

My Ruby is a little rusty, but it might look something like this for your SampleClass:

class SampleClass
   ...
   def eql? other
      other.instance_of?(self.class) 
         && @id == other.id
         && @name == other.name
   end

   # delegate to hash function of whatever primitive @id and @name are
   # probably Fixnum and String?
   # Uses two prime numbers, adapted from Effective Java by Joshua Bloch
   def hash 
      p, q = 17, 37
      p = q * @id.hash
      p = q * @name.hash
   end
   ...
end

Andere Tipps

This is all you need.

class SampleClass
   ...

   def hash 
      [@id,@name].hash
   end
   ...
end
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top