From: Rick DeNatale Date: 2007-09-18T01:24:52+09:00 Subject: Re: how to use tuple as hash key On 9/17/07, SpringFlowers AutumnMoon 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. 1) 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) 2) In most cases Ruby hashes capture a reference to a key when it's inserted. 3) 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/