[?] problem using set with hash objects


S

Stephen Bannasch

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?
 
Ad

Advertisements

S

Stefano Crocco

Alle luned=EC 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'
=3D> true
irb(main):002:0> a =3D [1,1,2,3]
=3D> [1, 1, 2, 3]
irb(main):003:0> b =3D [{:a1 =3D> "123"}, {:a1 =3D> "123"}, {:b1 =3D> "12= 3"}]
=3D> [{:a1=3D>"123"}, {:a1=3D>"123"}, {:b1=3D>"123"}]
irb(main):004:0> seta =3D a.to_set
=3D> #<Set: {1, 2, 3}>
irb(main):005:0> setb =3D b.to_set
=3D> #<Set: {{:a1=3D>"123"}, {:a1=3D>"123"}, {:b1=3D>"123"}}>
irb(main):006:0> b[0] =3D=3D b[1]
=3D> true

Am I doing something wrong?

According to the ri documentation, Set internally stores items in a hash.=20
Because of this, it uses the eql? and hash methods, and not =3D=3D, to test=
=20
objects for equality. Hash#eql? (actually, Kernel#eql?) only returns true i=
f=20
two objects are the same object. Since b[0] and b[1] are different objects,=
=20
Set considers them not equal, and thus stores them both. If you put the sam=
e=20
hash in two places of the array you convert to a set, only one of them will=
=20
be kept:

irb: 001> require 'set'
true
irb: 002> h =3D {'a' =3D> 1, 'b' =3D> 2}
{"a"=3D>1, "b"=3D>2}
irb: 003> a =3D [h, {'c' =3D> 3}, h]
[{"a"=3D>1, "b"=3D>2}, {"c"=3D>3}, {"a"=3D>1, "b"=3D>2}]
irb: 004> a.to_set.size
2
irb: 005> p a.to_set
#<Set: {{"a"=3D>1, "b"=3D>2}, {"c"=3D>3}}>

Other classes, instead, provide their own definition of eql?, which leads t=
o=20
different (often less surprising) results. For instance, Array#eql? returns=
=20
true if the two arrays have the same elements. String do the same.

I hope this helps

Stefano
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top