# Hashes, Sets, and eql?

**URL:** <https://www.ruby-forum.com/t/hashes-sets-and-eql/121132>\
**Category:** Ruby\
**Created:** [November 24, 2007, 7:34am UTC](https://www.ruby-forum.com/t/hashes-sets-and-eql/121132 "2007-11-24T07:34:03Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![aaronpowell](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/7bcc69/32.png) [@aaronpowell](https://www.ruby-forum.com/u/aaronpowell)\
**Post date:** [November 24, 2007, 7:34am UTC](https://www.ruby-forum.com/t/hashes-sets-and-eql/121132/1 "2007-11-24T07:34:03Z")

</div>

Hey everyone!

I’ve been working with sets and I need to use them as a key in a hash.  
Unfortunately, it looks like Sets are implemented as a Hash (which makes  
sense), but it looks like Set#eql? calls eql? on the underlying Hash.

This seems bad to me. It means that I can’t make two sets eql? to each  
other.

Take this irb session for example:

> > require ‘set’  
> > =\> true
> 
> > a = Set.new([1,2])  
> > =\> #\<Set: {1, 2}\>
> 
> > b = Set.new([1,2])  
> > =\> #\<Set: {1, 2}\>
> 
> > a.eql?(b)  
> > =\> false

I understand that sets are unordered, but it still seems weird to me  
that the preceding code won’t work. My workaround(?) for now is  
converting the set to an array and sorting the array by hash key, and  
keying on that:

> > a.to\_a.sort\_by { |x| x.hash }.eql?(b.to\_a.sort\_by { |x| x.hash })  
> > =\> true

---

<div class="post-metadata">

**Author:** ![aaronpowell](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/7bcc69/32.png) [@aaronpowell](https://www.ruby-forum.com/u/aaronpowell)\
**Post date:** [November 24, 2007, 7:55am UTC](https://www.ruby-forum.com/t/hashes-sets-and-eql/121132/2 "2007-11-24T07:55:40Z")

</div>

Hi Aaron,

Set#eql? is probably just Object#eql?, which, as I understand it would  
compare the actual objects (not their contents). What you want is a  
“shallow” comparison (==).

irb(main):002:0\> require ‘set’  
=\> true  
irb(main):003:0\> a = Set.new([1,2])  
=\> #\<Set: {1, 2}\>  
irb(main):004:0\> b = Set.new([1,2])  
=\> #\<Set: {1, 2}\>  
irb(main):005:0\> a == b  
=\> true

Regards,  
Jordan

---

<div class="post-metadata">

**Author:** ![aaronpowell](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/7bcc69/32.png) [@aaronpowell](https://www.ruby-forum.com/u/aaronpowell)\
**Post date:** [November 24, 2007, 8:27am UTC](https://www.ruby-forum.com/t/hashes-sets-and-eql/121132/3 "2007-11-24T08:27:13Z")

</div>

On Sat, Nov 24, 2007 at 03:55:00PM +0900, MonkeeSage wrote:

> Hi Aaron,
> 
> Set#eql? is probably just Object#eql?, which, as I understand it would  
> compare the actual objects (not their contents). What you want is a  
> “shallow” comparison (==).

Nope. I don’t want a shallow comparison. I need to use my sets as hash  
keys, so I need .eql? and .hash. Set calls eql? on the internal hash  
object.

From the set source:

def hash # :nodoc:  
@hash.hash  
end

def eql?(o) # :nodoc:  
return false unless o.is\_a?(Set)  
@hash.eql?(o.instance\_eval{@hash})  
end

---

<div class="post-metadata">

**Author:** ![aaronpowell](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/7bcc69/32.png) [@aaronpowell](https://www.ruby-forum.com/u/aaronpowell)\
**Post date:** [November 24, 2007, 12:40pm UTC](https://www.ruby-forum.com/t/hashes-sets-and-eql/121132/4 "2007-11-24T12:40:27Z")

</div>

On Nov 24, 1:25 am, Aaron P. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
wrote:

> Nope. I don’t want a shallow comparison. I need to use my sets as hash  
> keys, so I need .eql? and .hash. Set calls eql? on the internal hash object.

And eql? implies a.hash == b.hash. That is an object comparison, since  
Object#hash gives a unique value for every object like Object#id. What  
you need is shallow comparison of values rather than objects: a == b.  
I’m not sure how to easily make Hash do that.

Regards,  
Jordan

---

<div class="post-metadata">

**Author:** ![aaronpowell](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/7bcc69/32.png) [@aaronpowell](https://www.ruby-forum.com/u/aaronpowell)\
**Post date:** [November 24, 2007, 3:35pm UTC](https://www.ruby-forum.com/t/hashes-sets-and-eql/121132/5 "2007-11-24T15:35:50Z")

</div>

On Nov 24, 4:35 am, MonkeeSage [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> On Nov 24, 1:25 am, Aaron P. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)  
> wrote:
> 
> > Nope. I don’t want a shallow comparison. I need to use my sets as hash  
> > keys, so I need .eql? and .hash. Set calls eql? on the internal hash object.
> 
> And eql? implies a.hash == b.hash. That is an object comparison, since  
> Object#hash gives a unique value for every object like Object#id. What  
> you need is shallow comparison of values rather than objects: a == b.  
> I’m not sure how to easily make Hash do that.

Not always true:

irb(main):001:0\> a1 = [:a,1,‘foo’]  
=\> [:a, 1, “foo”]  
irb(main):002:0\> a2 = [:a,1,‘foo’]  
=\> [:a, 1, “foo”]  
irb(main):003:0\> a1.hash  
=\> 876036089  
irb(main):004:0\> a2.hash  
=\> 876036089

This discussion (using Hashes as hash or set keys, and the  
requirements and possible implementations of of eql? for this to work  
as sometimes desired) occurred in the last month or two on this list.

---

<div class="post-metadata">

**Author:** ![aaronpowell](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/7bcc69/32.png) [@aaronpowell](https://www.ruby-forum.com/u/aaronpowell)\
**Post date:** [November 24, 2007, 7:53pm UTC](https://www.ruby-forum.com/t/hashes-sets-and-eql/121132/6 "2007-11-24T19:53:31Z")

</div>

On Sat, Nov 24, 2007 at 11:35:05PM +0900, Phrogz wrote:

> > I’m not sure how to easily make Hash do that.  
> > =\> 876036089  
> > Yes. I guess my point is this: given the behavior of eql? and hash on  
> > Array, would you expect the same behavior from Set? I did, and found it  
> > surprising when they didn’t behave the same way.

Now, I can understand an argument against having the same behavior since  
Sets  
are unordered. But if that is the case, then why even implement .eql?  
and .hash on the Set class? Since those methods are implemented on Set,  
it leads me to believe that the original intent was for .eql? and .hash  
to behave the same way as on Array.

---

<div class="post-metadata">

**Author:** ![aaronpowell](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/a/7bcc69/32.png) [@aaronpowell](https://www.ruby-forum.com/u/aaronpowell)\
**Post date:** [November 24, 2007, 5:50pm UTC](https://www.ruby-forum.com/t/hashes-sets-and-eql/121132/7 "2007-11-24T17:50:33Z")

</div>

On Nov 24, 8:34 am, Phrogz [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> Not always true:
> 
> […]

Point taken. But still should not be relied on.

> This discussion (using Hashes as hash or set keys, and the  
> requirements and possible implementations of of eql? for this to work  
> as sometimes desired) occurred in the last month or two on this list.

Thanks, found it. [  
[http://groups.google.com/group/comp.lang.ruby/browse\_thread/thread/572e8d8b01a7d24e/c5f532d868afab05](http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/572e8d8b01a7d24e/c5f532d868afab05)  
].

So it seems the only way to do it is something like…

class Set  
def hash  
to\_a.sort.hash  
end  
def eql?(o)  
return false unless o.is\_a?(Set)  
@hash == o.instance\_eval{@hash}  
end  
end

…which is basically what Aaron was already doing.

Seems like there really should be a way to index keys on value rather  
than identity (even if it’s not the default or requires a separate  
class like someone on the other thread mentioned that Smalltalk has).

Regards,  
Jordan
