test = 'a'
test2 = '@a'.slice(0)
test3 = '@a'[1]
puts test.hash
puts test2.hash
puts test3.hash
Output:
100
64
97
Is this a bug or am I misunderstanding how the hash method works? Is there a way to fix this?
test = 'a'
test2 = '@a'.slice(0)
test3 = '@a'[1]
puts test.hash
puts test2.hash
puts test3.hash
Output:
100
64
97
Is this a bug or am I misunderstanding how the hash method works? Is there a way to fix this?
The reason is that each variable refers to different a object with its own unique hash code! The variable test
is the string "a", test2
is the integer 64 (the character number of '@'), and test3
is the integer 97 ('a'). The surprise is probably that in Ruby, the elements of strings are integers, not strings or characters.
The results of these expressions are not all the same data. Ruby 1.8 integers contain character numbers for single character indexing. This has been changed in Ruby 1.9, but slice(0)
returns the first character of the string '@'
, not 'a'
.
In Ruby 1.8 (using irb
):
irb(main):001:0> test = 'a'
=> "a"
irb(main):002:0> test2 = '@a'.slice(0)
=> 64
irb(main):003:0> test3 = '@a'[1]
=> 97
irb(main):004:0> test.hash
=> 100
irb(main):005:0> test2.hash
=> 129
irb(main):006:0> test3.hash
=> 195
In Ruby 1.9.1:
irb(main):001:0> test = 'a'
=> "a"
irb(main):002:0> test2 = '@a'.slice(0)
=> "@"
irb(main):003:0> test3 = '@a'[1]
=> "a"
irb(main):004:0> test.hash
=> 1365935838
irb(main):005:0> test2.hash
=> 347394336
irb(main):006:0> test3.hash
=> 1365935838
As maerics points out, unless you've defined your own hash method for the class you're using, the hash might simply be on the object itself, not its contents. That said, you can (and should) define your own hash method for any class where you define an equals method.
In Ruby, the String class already does this for you:
irb(main):001:0> test="a"
=> "a"
irb(main):002:0> test2="a"
=> "a"
irb(main):003:0> test.hash
=> 100
irb(main):004:0> test2.hash
=> 100
irb(main):005:0> test2[0]=test.slice(0)
=> 97
irb(main):006:0> test2
=> "a"
irb(main):007:0> test2.hash
=> 100
I haven't found an equivalent text for Ruby, but this page on Java gives an excellent algorithm for generating your own hash code that's not hard to copy for Ruby: http://www.javapractices.com/topic/TopicAction.do?Id=28