How can I initialize an Array inside a Hash in Ruby

24,078

Solution 1

@my_hash = Hash.new(Array.new)

This creates exactly one array object, which is returned every time a key is not found. Since you only ever mutate that array and never create a new one, all your keys map to the same array.

What you want to do is:

@my_hash = Hash.new {|h,k| h[k] = Array.new }

or simply

@my_hash = Hash.new {|h,k| h[k] = [] }

Passing a block to Hash.new differs from simply passing an argument in 2 ways:

  1. The block is executed every time a key is not found. Thus you'll get a new array each time. In the version with an argument, that argument is evaluated once (before new is called) and the result of that is returned every time.

  2. By doing h[k] = you actually insert the key into the hash. If you don't do this just accessing @my_hash[some_key] won't actually cause some_key to be inserted in the hash.

Solution 2

Try this:

@my_hash = Hash.new { |h, k| h[k] = Array.new }

Solution 3

The argument for Hash.new is for the default value for new hash keys, so when you pass it a reference that reference will be used for new hash keys. You're updating that reference when you call...

hash["key"].push "value"

You need to pass a new reference into the hash key before pushing values to it...

hash["key1"] = Array.new
hash["key1"].push "value1"
hash["key2"] = Array.new
hash["key2"].push "value2

You could try encapsulating this into a helper method as well.

Share:
24,078
jfanals
Author by

jfanals

Updated on July 09, 2022

Comments

  • jfanals
    jfanals almost 2 years

    I am trying to initialize a Hash of Arrays such as

    @my_hash = Hash.new(Array.new)
    

    so that I can:

    @my_hash["hello"].push("in the street")
    => ["in the street"]
    @my_hash["hello"].push("at home")
    => ["in the street", "at home"]
    @my_hash["hello"]
    =>["in the street", "at home"]
    

    The problem is that any new hash key also return ["in the street", "at home"]

    @my_hash["bye"]
    => ["in the street", "at home"]
    @my_hash["xxx"]
    => ["in the street", "at home"]
    

    !!!???

    What am I doing wrong what would be the correct way to initialize a Hash of Arrays?

  • mashe
    mashe over 13 years
    The one gotcha to be aware of here is that now even checking a key will create it, which may not be what you want. For this reason I sometimes create the Hash normally (with just {}), and then do the array-creation in the call, like (@myhash["hello"] ||= []).push("in the street").