Multilevel Hash Default Parameters When Accessing Non-Existant Keys as in h[1][1] += 1 [duplicate]
-
01-10-2019 - |
문제
This question already has an answer here:
I'm trying to create a Hash
of Hash
-es. The value of the 2nd level hash is an integer defaulted to 0. What I'm trying to do is start with an empty hash of hashes, and then when I add a value if the key doesn't exist, they key should be added with a default value.
When I try this with a simple Hash
of integers, it works fine:
irb(main):003:0> h = Hash.new(0)
=> {}
irb(main):004:0> h[1] += 1
=> 1
irb(main):005:0> p h
{1=>1}
=> nil
irb(main):006:0> h.keys.size
=> 1
irb(main):007:0>
h
now has one key and a value of 1. Perfect.
But when my hash's values are hashes, it doesn't appear to work:
irb(main):007:0> h = Hash.new(Hash.new(0))
=> {}
irb(main):008:0> h[1][1] += 1
=> 1
irb(main):009:0> p h
{}
=> nil
irb(main):010:0> h.keys.size
=> 0
irb(main):011:0>
Am I doing something wrong, or can I not set the default value for a hash to Hash.new(0)
?
EDIT:
Based on the answers below, I was able to figure out what I was doing wrong. Actually, I was able to figure out where my thinking was faulty. Long story short, h[1][1]
doesn't nest Hash.new
calls, unless you give Hash.new
a block of code to call. The value of the expression h[1][1] += 1
is 1
as expected, but only the innermost hash was being initialized properly.
I'm posting this because while my example above uses a 2-dimensional hash, my actual problem uses a 3-dimensional hash:
syms[level][exchange][symbol] = count
The solution might be helpful to others having this problem, so here's the code that gets this working the way I want:
irb(main):024:0> syms = Hash.new{|h1,k1| h1[k1] = Hash.new{|h2,k2| h2[k2] = Hash.new(0)}}
=> {}
irb(main):026:0> syms["level1"]["NYSE"]["IBM"] += 1
=> 1
irb(main):027:0> p syms
{"level1"=>{"NYSE"=>{"IBM"=>1}}}
=> nil
irb(main):028:0>
해결책
The most general way to set a default value when creating a Hash is to use the Hash#new function with a block argument:
h = Hash.new { |h, k| h[k] = Hash.new(0) }
The problem you have is that you are setting the default to a object pointer rather than a piece of code to be executed. That is, every time a missing value in the "outer" hash is accessed, it returns a pointer to the exact same object. To prove this to yourself, try this:
>> h = Hash.new(Hash.new(0))
=> {}
>> h[1] === h[2]
=> true
So you can in fact set the default value for a hash, but doing so will not accomplish what you want. Incrementing h[1][1] will in fact change the default hash itself, since h[1] hasn't been initialized.
Hope this helps.
다른 팁
h = Hash.new(Hash.new(0))
The Hash.new(0)is the default object; it's the same object for every nonexisting key. It's just what the hash returns when the key isn't there.
h = Hash.new{|h,k| h[k]=Hash.new(0)}
the {|h,k| h[k]=Hash.new(0)} is the default Proc. It runs when the key is absent and it creates a new hash (defaulting to 0.) for every non-existant key.