Stefano Crocco
9/17/2007 8:29:00 PM
Alle lunedì 17 settembre 2007, Stephen Bannasch ha scritto:
> I'm using set in the ruby standard library to produce collections of
> unique objects from enumerable objects with duplicates but it's
> doesn't appear to work with hash objects.
>
> $ ruby --version
> ruby 1.8.5 (2006-12-25 patchlevel 12) [i686-darwin8.9.1]
> $ irb
> irb(main):001:0> require 'set'
> => true
> irb(main):002:0> a = [1,1,2,3]
> => [1, 1, 2, 3]
> irb(main):003:0> b = [{:a1 => "123"}, {:a1 => "123"}, {:b1 => "123"}]
> => [{:a1=>"123"}, {:a1=>"123"}, {:b1=>"123"}]
> irb(main):004:0> seta = a.to_set
> => #<Set: {1, 2, 3}>
> irb(main):005:0> setb = b.to_set
> => #<Set: {{:a1=>"123"}, {:a1=>"123"}, {:b1=>"123"}}>
> irb(main):006:0> b[0] == b[1]
> => true
>
> Am I doing something wrong?
According to the ri documentation, Set internally stores items in a hash.
Because of this, it uses the eql? and hash methods, and not ==, to test
objects for equality. Hash#eql? (actually, Kernel#eql?) only returns true if
two objects are the same object. Since b[0] and b[1] are different objects,
Set considers them not equal, and thus stores them both. If you put the same
hash in two places of the array you convert to a set, only one of them will
be kept:
irb: 001> require 'set'
true
irb: 002> h = {'a' => 1, 'b' => 2}
{"a"=>1, "b"=>2}
irb: 003> a = [h, {'c' => 3}, h]
[{"a"=>1, "b"=>2}, {"c"=>3}, {"a"=>1, "b"=>2}]
irb: 004> a.to_set.size
2
irb: 005> p a.to_set
#<Set: {{"a"=>1, "b"=>2}, {"c"=>3}}>
Other classes, instead, provide their own definition of eql?, which leads to
different (often less surprising) results. For instance, Array#eql? returns
true if the two arrays have the same elements. String do the same.
I hope this helps
Stefano