Hash help

Attempting to convert this python code:
h = {}
for i, c in enumerate(“aabbcc”):
h.setdefault(c, []).append(i)
print(h)
#output => {‘a’: [0, 1], ‘b’: [2, 3], ‘c’: [4, 5]}

#to this crystal code
h = Hash(Char, Array(Int32)).new( Array(Int32).new )
“aabbcc”.each_char_with_index { |c, i|
h[c] << i
puts “h[#{c}]=#{h[c]}”
}
puts h[‘z’]
#output =>
h[a]=[0]
h[a]=[0, 1]
h[b]=[0, 1, 2]
h[b]=[0, 1, 2, 3]
h[c]=[0, 1, 2, 3, 4]
h[c]=[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5]

Is there a bug or am I misunderstanding. The puts h[‘z’] output looks especially suspicious…

You probably want something like:

h = Hash(Char, Array(Int32)).new { |hash, key| hash[key] = Array(Int32).new }
 
"aabbcc".each_char_with_index do |c, i|
  h[c] << i
  puts "h[#{c}]=#{h[c]}"
end
 
 
puts h # => {'a' => [0, 1], 'b' => [2, 3], 'c' => [4, 5]}
puts h['z'] # => []

I’m pretty sure what’s happening is you are using https://crystal-lang.org/api/Hash.html#new(default_value:V,initial_capacity=nil)-class-method that overload. This is creates an array that is to be used as the value of the hash when a key is not known. Since arrays are a reference type, the same one is used in each case there is an unknown key. As such all the values end up in this array, and nothing in the actual hash.

My example works because it’s setting the value of the missing key to a new empty array. I.e. the first time it’s called it gets created and the first item gets added to it. The next times it exists and is added as is. 'z' doesn’t exist, so a new empty array is returned correctly.

1 Like

Yes that works fine, thanks for the clear explanation.