Take the 2-minute tour ×

I first ran the below Part I of code and get the desired output, now just to play around it and to do some research I did Part II.

Part I

irb(main):001:0> h1 = { "a" => 100, "b" => 200, :c => "c" }
irb(main):002:0> h1["a"]
=> 100
irb(main):002:0> h1[:c]
=> "c"

Part II

irb(main):003:0> h1.compare_by_identity
irb(main):004:0> h1.compare_by_identity?
=> true
irb(main):005:0> h1["a"]
=> nil
irb(main):006:0> h1[:c]
=> "c"

How does h1["a"] give different values in Part I and Part II but not the same happened with h1[:c]?

I’m using Ruby 1.9.3.

share|improve this question

1 Answer 1

up vote 7 down vote accepted

The docs for compare_by_identity say this:

Makes hsh compare its keys by their identity, i.e. it will consider exact same objects as same keys.

Normally, hash keys will be matched using eql?, but compare_by_identity will instead match them using equal? (which is roughly equivalent to comparing object_ids*). Since different instances of a string with the same value have different object_ids, it doesn’t match. However, symbols always have the same object_id, so it does continue to match.

hash = { 'a' => 'str a', 'b' => 'str b', :c => 'sym c' }

hash.keys.map(&:object_id)      #=> [70179407935200, 70179407935180, 358408]
['a', 'b', :c].map(&:object_id) #=> [70179405705480, 70179405705460, 358408]

hash.keys.zip(['a', 'b', :c]).map { |pair| pair.inject(:eql?) }   #=> [true, true, true]
hash.keys.zip(['a', 'b', :c]).map { |pair| pair.inject(:equal?) } #=> [false, false, true]

a_key = hash.keys.first  #=> 'a'
hash['a']   #=> nil
hash[a_key] #=> 'str a'
hash[:c]    #=> 'sym c'

As you can see, the object_ids for the strings do not match the object_ids of the keys in the hash, but the symbol does. In fact, if you run 'a'.object_id & 'b'.object_id (or call object_id on any string) repeatedly, you will get a different value each time.

*The caveat here is that one could override object_id, but it will have no effect on the comparison since equal? doesn’t actually use object_id. Further, redefining equal? will change this, but doing so is not recommended explicitly in the Ruby docs.

share|improve this answer
+1 to you thanks for the explanations! may i have some vote up? :) –  Arup Rakshit Jan 13 '13 at 16:55
I just finished up your explanations and OMG Its awesome!! Nice explanation once more I have to say.Now i got clear concept on that! –  Arup Rakshit Jan 13 '13 at 17:26
This is wrong. Hash never uses == for equality. It uses either eql? or equal? depending on the setting of compare_by_identity. –  Jörg W Mittag Jan 13 '13 at 20:23
@JörgWMittag You’re correct, and I’ve updated my answer to reflect that—thanks! I think my error was mostly in glancing too quickly at the docs for Object#eql? which shows it as being the same as equal? and ==, and wrongly ignoring that they are usually overridden separately. –  Andrew Marshall Jan 13 '13 at 20:52
@VBSlover For those two, it pairs up the values in hash.keys and the passed array, then it calls eql?/equal? with that pair (in this case, the map could also be .map { |a, b| a.eql? b }, which may be clearer). It’s probably best to look at the intermediate values generated (i.e., look at what just the zip call returns). eql? & equal? are used for the reason I give in the answer: “Normally, hash keys will be matched using eql?, but compare_by_identity will instead match them using equal?”, so I compare the result of using each. –  Andrew Marshall Jan 13 '13 at 22:16

Your Answer


