Set with custom rule
ruby, ruby-on-rails
Solution
The docs that you link to say explicitly (emphasis mine):
The equality of each couple of elements is determined according to `Object#eql?` and `Object#hash`, since `Set` uses `Hash` 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.
Problem
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?