Sunil
Sunil

Reputation: 3434

Ruby: Properties of a hash key

I will just paste down a simple example i tried so that it would be clear to those who read this.

irb(main):001:0> h =  { }
=> {}
irb(main):002:0> a=[1,2,3]
=> [1, 2, 3]
irb(main):003:0> a.object_id
=> 69922343540500


irb(main):004:0> h[a] = 12          #Hash with the array as a key
=> 12
irb(main):005:0> a << 4             #Modified the array
=> [1, 2, 3, 4]
irb(main):006:0> a.object_id        #Object id obviously remains the same.
=> 69922343540500
irb(main):007:0> h[a]               #Hash with the same object_id now returns nil.
=> nil
irb(main):008:0> h                  #Modified hash
=> {[1, 2, 3, 4]=>12}
irb(main):009:0> h[[1,2,3,4]]       #Tried to access the value with the modified key -
=> nil


irb(main):011:0> h.each { |key,value| puts "#{key.inspect} maps #{value}" }
[1, 2, 3, 4] maps 12
=> {[1, 2, 3, 4]=>12}

Now when i iterate over the hash, its possible to identify the map between the key and the value.

Could some one please explain me this behaviour of ruby hash and what are the properties of hash keys.

1) As i mentioned above, the object_id hasn't changed - then why is the value set to nil.

2) Is there any possible way so that i can get back the value '12' from the hash 'h' because h[[1,2,3,4]] as mentioned above returns nil.

Upvotes: 4

Views: 1420

Answers (4)

rubyprince
rubyprince

Reputation: 17793

This happens because the key should not have its value changed while it is in use. If the value changes, we should rebuild the hash, based on its current value. Look at Ruby API for rehash method. You can get back the value, by rebuilding the hash again after the key is changed, like this:

irb(main):022:0> h.rehash
=> {[1, 2, 3, 4]=>12}
irb(main):023:0> h[a]
=> 12

Upvotes: 8

gunn
gunn

Reputation: 9165

Stefaan Colman's answer is more thorough, but a couple of observations:

Ruby uses the Object#hash method to hash objects.

You can get 12 back out like by doing a.delete(4); h[a] at that point, [1,2,3] is also able to be used as a key again.

Upvotes: 1

Stefaan Colman
Stefaan Colman

Reputation: 3705

The ruby hash api provides the answer: the key should not have its value changed while it is in use as a key.

I guess intern a hash is calculated for a and used for quick lookup (because a key shouldn't change, the hash is always the same). So when you do h[a] it doesn't find a match ([1,2,3].hash != [1,2,3,4].hash) and when you do h[[1,2,3]] the hashes match but the object doesn't match ([1,2,3] != [1,2,3,4]).

A fix is to use the object_id as key because it doesn't change, h[a.object_id] = 12 will return 12 when a changes. Ofcourse this has the downside that h[[1,2,3].object_id] won't return 12.

Upvotes: 1

Gareth
Gareth

Reputation: 138082

Hash keys are checked using the #eql? method, and since [1, 2, 3] isn't .eql? to [1, 2, 3,4] your hash lookup has a different result.

Maybe you want to be using something other than an Array as your Hash key if the semantics aren't working for you?

Upvotes: 1

Related Questions