10

I have two strings that appear equal:

context = "Marriott International World’s Most ADMIRED Lodging Company by FORTUNE for 14th yr. via @FortuneMagazine http://cnnmon.ie/1kcFZSQ"
slice_str = context.slice(105,24) # => "http://cnnmon.ie/1kcFZSQ"
str = "http://cnnmon.ie/1kcFZSQ"

slice_str == str                  # => true
slice_str.eql? str                # => true

But when I look up values in a hash where the keys are the strings, they do not return the same thing in Ruby 2.1.0 and Ruby 2.1.1:

redirects = {"http://cnnmon.ie/1kcFZSQ"=>""}
redirects.key?(slice_str)         # => false
redirects.key?(str)               # => true

What explanation is there for this behaviour? Ruby 1.9.3 works as expected.

sawa
  • 165,429
  • 45
  • 277
  • 381
Rich Sutton
  • 10,004
  • 1
  • 17
  • 21
  • 1
    Hmmm. And `redirects.keys.include? slice_str` is `true` – Michael Berkowski May 30 '14 at 02:37
  • The above one is worked fine for me returning true value for `redirects.key?(slice_str)` try again once. – anusha May 30 '14 at 02:43
  • @anusha Did you retype or copy/paste it? What ruby version? – Michael Berkowski May 30 '14 at 02:43
  • @Michael Berkowski: I just copied your code and checked it worked for me – anusha May 30 '14 at 02:49
  • @Michael Berkowski: Sorry fr my mistake michael actually i have checked it in ruby 1.9.3 it worked fine but in ruby 2.1.1 i am getting same result as of you – anusha May 30 '14 at 02:56
  • 5
    Apparently this is a bug in ruby 2.0, it was reported here -https://bugs.ruby-lang.org/issues/9882 and fixed here https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/46243... – Uri Agassi May 30 '14 at 06:02
  • @UriAgassi That's impressive turn around from the Ruby maintainers. Post this as the answer and I'll accept it. – Rich Sutton May 30 '14 at 07:10
  • 2
    Actually, you should ask @sawa to do it, since I only copied this information from his deleted answer... – Uri Agassi May 30 '14 at 07:32
  • Does the community have any policy or opinion on what the actual correct answer here is? It's a bug in Ruby that's been fixed, but if you are stuck on a current 2.x version, the workaround is also a "fix". – Rich Sutton May 30 '14 at 17:52

2 Answers2

2

This was a bug in ruby < 2.1.3

$ rvm use 2.1.2
Using /Users/richniles/.rvm/gems/ruby-2.1.2
$ irb
2.1.2 :001 > context = "Marriott International World’s Most ADMIRED Lodging Company by FORTUNE for 14th yr. via @FortuneMagazine http://cnnmon.ie/1kcFZSQ"
 => "Marriott International World’s Most ADMIRED Lodging Company by FORTUNE for 14th yr. via @FortuneMagazine http://cnnmon.ie/1kcFZSQ" 
2.1.2 :002 > slice_str = context.slice(105,24) # => "http://cnnmon.ie/1kcFZSQ"
 => "http://cnnmon.ie/1kcFZSQ" 
2.1.2 :003 > str = "http://cnnmon.ie/1kcFZSQ"
 => "http://cnnmon.ie/1kcFZSQ" 
2.1.2 :004 > redirects = {"http://cnnmon.ie/1kcFZSQ"=>""}
 => {"http://cnnmon.ie/1kcFZSQ"=>""} 
2.1.2 :005 > redirects.key?(slice_str) 
 => false 
2.1.2 :006 > redirects.key?(str)  
 => true 

but do the same in ruby 2.1.3:

 $ rvm use 2.1.3
 Using /Users/richniles/.rvm/gems/ruby-2.1.3
 $ irb
 2.1.3 :001 > context = "Marriott International World’s Most ADMIRED Lodging Company by FORTUNE for 14th yr. via @FortuneMagazine http://cnnmon.ie/1kcFZSQ"
  => "Marriott International World’s Most ADMIRED Lodging Company by FORTUNE for 14th yr. via @FortuneMagazine http://cnnmon.ie/1kcFZSQ" 
 2.1.3 :002 > slice_str = context.slice(105,24) # => "http://cnnmon.ie/1kcFZSQ"
  => "http://cnnmon.ie/1kcFZSQ" 
 2.1.3 :003 > str = "http://cnnmon.ie/1kcFZSQ"
  => "http://cnnmon.ie/1kcFZSQ" 
 2.1.3 :004 > redirects = {"http://cnnmon.ie/1kcFZSQ"=>""}
  => {"http://cnnmon.ie/1kcFZSQ"=>""} 
 2.1.3 :005 > redirects.key?(slice_str) 
  => true 
 2.1.3 :006 > redirects.key?(str)  
  => true 
Rich Niles
  • 76
  • 8
0

For Hash keys, its key#hash method determines, whether keys considered equal or not.

In ruby 2.1.1 for your example those two string hashes are different:

slice_str.hash == str.hash # => false in Ruby 2.1.1

Though, it's totally unclear to me, why sliced string has different hash. Even stranger -- I've found if you test the code on ASCII-only string (your string, but with ' instead of ) -- the hashes will be the same!

It's really weird.

The only solution I've found (though it doesn't look elegant at all):

slice_str = context.slice(105,24).chars.join # split it into separate chars and then join back
p str.hash == slice_str.hash # true now
p redirects.key?(slice_str) # true now

UPD: Oops, I haven't saw link to bug in the comments above :(

zverok
  • 1,290
  • 1
  • 9
  • 13
  • I don't know why @sawa withdrew his answer, but he found `slice` was having a problem with the multi-byte character; the difference in hash values was just a by-product. – Cary Swoveland May 30 '14 at 16:58
  • @CarySwoveland. yeah, I see now. Though, I haven't withdrew my answer just because it provides some (really dumb) workaround. – zverok May 30 '14 at 23:08