# Ruby hash equlity

**URL:** <https://www.ruby-forum.com/t/ruby-hash-equlity/84970>\
**Category:** Ruby\
**Created:** [February 8, 2007, 10:31pm UTC](https://www.ruby-forum.com/t/ruby-hash-equlity/84970 "2007-02-08T22:31:16Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [February 8, 2007, 10:31pm UTC](https://www.ruby-forum.com/t/ruby-hash-equlity/84970/1 "2007-02-08T22:31:16Z")

</div>

Based on the description of Hash#==

" Equality—Two hashes are equal if they each contain the same  
number of keys and if each key-value pair is equal to (according to  
+Object#==+) the corresponding elements in the other hash."

I am having problems understanding the following:

$ irb  
irb(main):001:0\> VERSION  
=\> “1.8.5”  
irb(main):002:0\> a={:c =\> nil, :d =\> nil}  
=\> {:c=\>nil, :d=\>nil}  
irb(main):003:0\> b={:d =\> nil, :c =\> nil}  
=\> {:c=\>nil, :d=\>nil}  
irb(main):004:0\> # As expected this is true  
irb(main):005:0\* a == b  
=\> true  
irb(main):006:0\> c={:a =\> nil, a =\> nil, :b =\> nil}  
=\> {:b=\>nil, :a=\>nil, {:c=\>nil, :d=\>nil}=\>nil}  
irb(main):007:0\> d={:b =\> nil, b =\> nil, :a =\> nil}  
=\> {:b=\>nil, {:c=\>nil, :d=\>nil}=\>nil, :a=\>nil}  
irb(main):008:0\> # Unexpectedly false  
irb(main):009:0\* c == d  
=\> false

Shouldn’t they be equivalent ?

Thanks,

Mike

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [February 8, 2007, 11:06pm UTC](https://www.ruby-forum.com/t/ruby-hash-equlity/84970/2 "2007-02-08T23:06:20Z")

</div>

On Feb 8, 2:29 pm, removed\_email\_address@domain.invalid wrote:

> irb(main):005:0\* a == b  
> =\> true  
> irb(main):006:0\> c={:a =\> nil, a =\> nil, :b =\> nil}  
> =\> {:b=\>nil, :a=\>nil, {:c=\>nil, :d=\>nil}=\>nil}  
> irb(main):007:0\> d={:b =\> nil, b =\> nil, :a =\> nil}  
> =\> {:b=\>nil, {:c=\>nil, :d=\>nil}=\>nil, :a=\>nil}  
> irb(main):008:0\> # Unexpectedly false  
> irb(main):009:0\* c == d  
> =\> false
> 
> Shouldn’t they be equivalent ?

Let’s make your example a little bit simpler to get at the core of the  
matter:  
a = {}  
b = {}  
p a==b  
#=\> true

c = {1=\>a}  
d = {1=\>b}  
p c==d  
#=\> true

e = {a=\>1}  
f = {b=\>1}  
p e==f  
#=\> false

Apparently it’s enough for two values to be #== to one another, but  
it’s not good enough for two keys to be #== to one another.

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [February 8, 2007, 11:16pm UTC](https://www.ruby-forum.com/t/ruby-hash-equlity/84970/3 "2007-02-08T23:16:21Z")

</div>

On Feb 8, 3:00 pm, “Phrogz” [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > =\> {:c=\>nil, :d=\>nil}  
> > irb(main):009:0\* c == d
> 
> Apparently it’s enough for two values to be #== to one another, but  
> it’s not good enough for two keys to be #== to one another.

To take it one step further, think about it this way:  
p c[1] == d[1]  
#=\> true

p e[a] == f[a]  
#=\> false

If you wanted the functionality you were expecting, then for these two  
hashes:  
x = { m=\>1, n=\>2, o=\>3 }  
y = { g=\>1, h=\>2, k=\>3 }  
testing for equality would have to do something like:

class Hash

# Ouch! O(n^2) performance

# Very bad for hashes with many keys

def sort\_of\_equal( other )  
equal = true  
self.each{ |k,v|  
found\_key=false  
other.each{ |k2,v2|  
found\_key ||= ( ( k==k2 ) && ( v==v2 ) )  
}  
equal &&= found\_key  
}  
equal  
end  
end

p e.sort\_of\_equal( f )  
#=\> true

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [February 9, 2007, 12:05am UTC](https://www.ruby-forum.com/t/ruby-hash-equlity/84970/4 "2007-02-09T00:05:24Z")

</div>

Finally, here’s a slightly better version. It’s still O(n^2) in the  
worst case, but should perform much better under common circumstances  
(in case someone actually needed this functionality):

class Hash

# Ouch! O(n^2) performance

# Bad for hashes with many keys

def sort\_of\_equal( other )  
equal = true  
self.each{ |k,v|  
unless found\_key = (other[k]==v)  
other.each{ |k2,v2|  
break if found\_key = ( ( k==k2 ) && ( v==v2 ) )  
}  
end  
equal &&= found\_key  
}  
equal  
end  
end

Note that this will still not work if you have nested hashes that you  
want to treat like this. You’d need to override Hash#== fully, or put  
in tests based on key and value type.

---

<div class="post-metadata">

**Author:** ![unknown](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/u/4bbf92/32.png) [@unknown](https://www.ruby-forum.com/u/unknown)\
**Post date:** [February 9, 2007, 2:25pm UTC](https://www.ruby-forum.com/t/ruby-hash-equlity/84970/5 "2007-02-09T14:25:36Z")

</div>

Thanks Phrogz, but yes my problem would require nested hashes and  
would have  
to be reasonably efficient. Let me explain… I started with the  
problem of comparing  
sets that may have sets as members:

irb(main):017:0\> require ‘set’  
=\> false  
irb(main):018:0\> Set[:a, :b] == Set[:a, :b]  
=\> true  
irb(main):019:0\> Set[:a, :b, Set[:c, :d]] == Set[:a, :b, Set[:c, :d]]  
=\> false

Suprisingly it didn’t work. So I looked at how sets were implemented -  
basically  
hashes where the set members are the keys and the values are true.

I still believe that hash equality should be as the ‘ri’ manual  
describes. I’m going  
to examine the ruby ‘C’ code to see exactly why…

Mike
