Robert Klemme
1/26/2006 1:15:00 PM
Michael McGreevy wrote:
> Hello all,
>
> I would like to store arrays in a hash, indexed by a string key. I
> would like to have the hash create an empty array as the default
> value, when it sees a new key, something like the code involving
> "hash1" below. However, this code is giving some strange results --
> it claims that the hash is empty, even though there is an array
> stored in it, and I can then retrieve that array.
>
> I am new to ruby, so maybe I am just doing something stupid (... I am
> not sure about that "Hash.new( [] )" for example... )
>
> Can anyone explain these results?
Yes. You ran into the typical Hash pitfal: the default value is the one
returned if something is not found for the given key. But it never
changes the hash and there is just this single instance. Consider this:
>> h=Hash.new([])
=> {}
>> h[0]<<1
=> [1]
>> h[0]
=> [1]
>> h["foo"]
=> [1]
>> h["bar"] << 2
=> [1, 2]
>> h[:x]
=> [1, 2]
>> h.default
=> [1, 2]
It works for numeric values because then one usually assigns:
>> h=Hash.new(0)
=> {}
>> h[:foo] += 1
=> 1
>> h[:bar] += 10
=> 10
>> h
=> {:bar=>10, :foo=>1}
Note the "+=" contains an assignment and it's equivalent to
h[:foo] = h[:foo] + 1
You on the other hand want the block form because that can do arbitrary
things when a key is not found:
>> h=Hash.new() {|ha,key| puts "missing #{key}"; ha[key] = []}
=> {}
>> h[:foo] << "foo"
missing foo
=> ["foo"]
>> h[:bar] << "foo"
missing bar
=> ["foo"]
>> h[:foo] << "foo end"
=> ["foo", "foo end"]
>> h[:foo] << "foo more"
=> ["foo", "foo end", "foo more"]
>> h
=> {:bar=>["foo"], :foo=>["foo", "foo end", "foo more"]}
Kind regards
robert