According to the Set doc, elements in a set are compared using eql?
.
I have a class like:
class Foo
attr_accessor :bar, :baz
def initialize(bar = 1, baz = 2)
@bar = bar
@baz = baz
end
def eql?(foo)
bar == foo.bar && baz == foo.baz
end
end
In console:
f1 = Foo.new
f2 = Foo.new
f1.eql? f2 #=> true
But...
s = Set.new
s << f1
s << f2
s.size #=> 2
Because f1
equals f2
, s
should not include both of them.
How to make the set
reject elements with a custom rule?
The docs that you link to say explicitly (emphasis mine):
The equality of each couple of elements is determined according to
Object#eql?
andObject#hash
, sinceSet
usesHash
as storage.
If you add a hash
method to your class that returns the same value for eql?
objects, it works:
# With your current class
f1, f2 = Foo.new, Foo.new
p f1.eql?(f2)
#=> true
p f1.hash==f2.hash
#=> false
p Set[f1,f2].length
#=> 2
# Fix the problem
class Foo
def hash
[bar,hash].hash
end
end
f1, f2 = Foo.new, Foo.new
p f1.eql?(f2)
#=> true
p f1.hash==f2.hash
#=> true
p Set[f1,f2].length
#=> 1
To be honest I've never had a great sense for how to write a good custom hash
method when multiple values are involved.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With