Bug #992

Hash becomes Array for no apparent reason.

Added by loqi (Loqi Tamaroon) almost 11 years ago. Updated over 8 years ago.

Target version:
ruby -v:


# Using Ruby 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0]
# on Mac OS X 10.5.5
def is_this_a_ruby_bug?
orig_hash = { :a=>[:x], :b=>[:x] }
orig_hash.inject({}) do | flipped_hash, (symbol_a_or_b, array_x) |
array_x.each do |symbol_x|
(flipped_hash[symbol_x] ||= []) << symbol_a_or_b # (~1)
flipped_hash # (~2)
end # (~3)
# flipped_hash # (~4)
# The above method is supposed to return { :x => [:a, :b] } . Instead, it exhibits
# bizarre behavior. On the first iteration of the inner loop, line (~2) somehow causes
# flipped_hash to be recast from Hash {:x=>[:a]} to Array [:x] . This causes (~1) to
# raise a "Symbol as array index (TypeError)" exception on the second iteration of
# array_x.each , because flipped_hash has accedentally become an Array. A workaround
# is to uncomment (~4), which curiously is OUTSIDE the inner loop where the exception
# would've occured. The effect is that the 'end' at (~3) no longer terminates the inner
# loop and (~4) is now executed with each iteration of array_x.each as if it were above
# (~3). Somehow this fixes the problem, and the method returns the expected result.



Updated by shyouhei (Shyouhei Urabe) almost 11 years ago

  • Status changed from Open to Rejected

No it isn't. Your "workaround" is the canonical way. See whatever documentation you like that describes inject method.


Updated by mernen (Daniel Luz) almost 11 years ago

Since Enumerable#each ignores the return value of the block, (~2) is a no-op. #each returns the original object (in this case, array_x), and as such you're passing the second iteration of #inject array_x, not flipped_hash. (~4) is the correct position to return a value here.


Updated by loqi (Loqi Tamaroon) almost 11 years ago

Hey thanks Daniel. I missed that detail about #each . I'm new to Ruby.


Also available in: Atom PDF