On 9/17/07, SpringFlowers AutumnMoon [email protected] wrote:
in Python, a hash key cannot be [1,2,3] but must be (1,2,3), a tuple.
in Ruby, a list can actually be used as a hash key… but is it true
that it is using the list’s ID as the key, not the list’s content…
Not sure that this is true, since I don’t understand what you mean.
(that’s why in Python, tuples can be used as hash keys, since they are
unique by the values they contain)
what about in Ruby, since there is no tuple, how can you similate using
tuple as hash key? using list.join(“,”) as a key?
There’s a better way. You can use just about any object as a key in a
ruby hash, but you need to be aware of a few key things.
-
Ruby hashes use two methods on a key to insert it or to search
for it. Those two methods are
hash and eql? Two objects are considered the same as far as the
hash is concerned if first
their hash values are equal, and second if they compare using
the :eql? method. When you
insert into a hash using a key, it’s your responsibility to
ensure that if two objects are :eql? they
will return the same hash value. Ruby arrays (and most if not
all standard Ruby objects do)
-
In most cases Ruby hashes capture a reference to a key when it’s
inserted.
-
If you change an object which is referenced as a key in a hash in
such a way that it’s hash
value changes, then the hash won’t know about it without help,
and you’ll likely be surprised.
Here’s some code to walk through to illustrate the above:
Make two different arrays, referenced by variables a and b
respectively
a = [1, 2, 3]
b = [1, 2, 3]
They are eql
a.eql? b # => true
But not the same object
a.equal? b # => false
a.hash .eql? b.hash # => true
Now create a hash using a as a key
h = {a => “Fred”}
We can retrieve using the object referenced by a
h[a] # => “Fred”
And using the variable referenced by b
h[b] # => “Fred”
Or using an new literal array which is eql
h[[1,2,3]] # => “Fred”
Note that a and b STILL refer to different objects
a.eql? b # => true
a.equal? b # => false
Note also that hash.keys does not return the original object.
h.keys.first.eql? a # => true
h.keys.first.eql? b # => true
Now lets change a
a << 4
a and b should no longer be eql
a.eql? b # => false
What happens if we access the hash now
h[a] # => nil
And using the variable referenced by b
h[b] # => nil
Or using an new literal array which is eql
h[[1,2,3]] # => nil
What’s happened is that the hash actually DID capture the key
originally.
The internal key changed so we can’t find a value using [1,2,3]
Notice that none of the above found a value even h[a], despite the
fact that a does
in fact reference the new key:
a # => [1, 2, 3, 4]
And we can’t find it with an eql literal array either
h[[1,2,3,4]] # => nil
The hash didn’t know about the change to the key, so the hashing is
out of sync with reality
Rehashing the hash rights things:
h.rehash
h[[1,2,3,4]] # => “Fred”
So if we WANT to allow changing keys after putting them in a hash we
need to rehash after a change
If we don’t want to allow changing keys, then dup the key when you
put it in the hash.
h2 = {a.dup => “Ethel”}
h2[a] # => “Ethel”
a # => [1, 2, 3, 4]
h2[[1,2,3,4]] #=>
a << 5
Since a has changed we don’t expect to find it
h2[a] # => nil
But since we duped it the key in the hash didn’t change
h2[[1,2,3,4,5]] # => nil
h2[[1,2,3,4]] # =>
–
Rick DeNatale
My blog on Ruby
http://talklikeaduck.denhaven2.com/