ruby hash .defaultをリストに設定する[複製]
質問
この質問にはすでに回答があります:
デフォルトのメソッドがハッシュに対して行うことを理解したと思います...
キーが存在しない場合、キーのデフォルト値を指定します:
irb(main):001:0> a = {}
=> {}
irb(main):002:0> a.default = 4
=> 4
irb(main):003:0> a[8]
=> 4
irb(main):004:0> a[9] += 1
=> 5
irb(main):005:0> a
=> {9=>5}
すべて良い。
ただし、デフォルトを空のリストまたは空のハッシュに設定すると、すべてでの動作がわかりません。...
irb(main):001:0> a = {}
=> {}
irb(main):002:0> a.default = []
=> []
irb(main):003:0> a[8] << 9
=> [9] # great!
irb(main):004:0> a
=> {} # ?! would have expected {8=>[9]}
irb(main):005:0> a[8]
=> [9] # awesome!
irb(main):006:0> a[9]
=> [9] # unawesome! shouldn't this be [] ??
|| =演算子を使用した場合と同じ動作を期待/期待していました...
irb(main):001:0> a = {}
=> {}
irb(main):002:0> a[8] ||= []
=> []
irb(main):003:0> a[8] << 9
=> [9]
irb(main):004:0> a
=> {8=>[9]}
irb(main):005:0> a[9]
=> nil
誰が何が起こっているのか説明できますか?
解決
Hash.default
は、存在しないキーを照会するときにデフォルト値 returned を設定するために使用されます。コレクション内のエントリは、クエリされたという理由だけで作成されません。
また、default
に設定する値はオブジェクト(あなたの場合は配列)のインスタンスであるため、これが返されたときに操作できます。
a = {}
a.default = [] # set default to a new empty Array
a[8] << 9 # a[8] doesn't exist, so the Array instance is returned, and 9 appended to it
a.default # => [9]
a[9] # a[9] doesn't exist, so default is returned
他のヒント
これは非常に便利なイディオムです:
(myhash[key] ||= []) << value
ネストすることもできます:
((myhash[key1] ||= {})[key2] ||= []) << value
別の方法は次のとおりです。
myhash = Hash.new {|hash,key| hash[key] = []}
しかし、これはキーについて尋ねることでキーが作成されるという重大な副作用があります。これはhas_keyをレンダリングしますか?かなり役に立たないので、この方法は避けます。
これはあなたが探している行動だと思います。これにより、ハッシュ内の新しいキーが配列に自動的に初期化されます。
irb(main):001:0> h = Hash.new{|h, k| h[k] = []}
=> {}
irb(main):002:0> h[1] << "ABC"
=> ["ABC"]
irb(main):003:0> h[3]
=> []
irb(main):004:0> h
=> {1=>["ABC"], 3=>[]}
glenn mcdonaldのコメント:
<!> quot;他の方法は次のとおりです:
myhash = Hash.new {| hash、key | hash [key] = []}
しかし、これはキーについて尋ねるとキーが作成され、has_keyをレンダリングするという重大な副作用がありますか?かなり役に立たないので、この方法は避けます。<!> quot;
実際にはそうではないようです。
irb(main):004:0> a = Hash.new {|hash,key| hash[key] = []}
=> {}
irb(main):005:0> a.has_key?(:key)
=> false
irb(main):006:0> a[:key]
=> []
irb(main):007:0> a.has_key?(:key)
=> true
予想どおり、キーにアクセスするとキーが作成されます。 has_keyを要求するだけですか?ではない。
本当に無限に深いハッシュが必要な場合:
endless = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
endless["deep"]["in"]["here"] = "hello"
もちろん、グレンが上で指摘したように、これを行うと、has_key?常にtrueを返すため、その意味を失います。このためのjbarnetteへのThx。
irb(main):002:0> a.default = []
=> []
irb(main):003:0> a[8] << 9
=> [9] # great!
このステートメントで、デフォルトを変更しました。新しい配列を作成し、<!> quot; 9 <!> quot;を追加していない。この時点では、代わりにこれを実行した場合と同じです:
irb(main):002:0> a.default = [9]
=> [9]
したがって、これを取得しても驚くことではありません:
irb(main):006:0> a[9]
=> [9] # unawesome! shouldn't this be [] ??
さらに、「<!> lt; <!> lt;」配列に「9」を追加しました。ハッシュに追加しなかったので、これが説明されています:
irb(main):004:0> a
=> {} # ?! would have expected {8=>[9]}
.defaultを使用する代わりに、おそらくあなたのプログラムでしたいことは次のようなものです:
# Time to add a new entry to the hash table; this might be
# the first entry for this key..
myhash[key] ||= []
myhash[key] << value
これがあなたの望むものかどうかはわかりませんが、これを行うと、欠落しているハッシュキーがクエリされたときに常に空の配列を返すことができます。
h = Hash.new { [] }
h[:missing]
=> []
#But, you should never modify the empty array because it isn't stored anywhere
#A new, empty array is returned every time
h[:missing] << 'entry'
h[:missing]
=> []