多段階の Hash を 1 回の処理で欲しかったが、やり方がピンと来ない。
調べてみてそのまま使えば、動きはしたものの「コピペ」には責任が伴うものであり、その内容を詳しく見てみることにした。
参考
欲しかったもの
段階を踏まずに 1 回で深いところの hash まで設定できるようにしたい。
1 | # このコードは動かない |
欲しかったもの - その答え
多段のハッシュを 1 回で得るには、次の記述でよい。
1 | h = Hash.new { |hash,key| hash[key] = Hash.new(&hash.default_proc) } |
何をしているのか、釈然としないので、いろいろ調べる
Ruby の Hash クラスには、ブロックを渡すことができる。
ブロックを渡すと、対応するキーがなかったときに、呼び出しされる処理を定義できる。
このブロック形式のデフォルト値のことをデフォルトブロックと記述されていることがある。
設定されていないときは、nil が返る。
1 | h = Hash.new |
試しに、設定してみる。
設定すると、.default_proc
が Proc を返す。
1 | h = Hash.new {|hash,key| p key} |
読んでいくと、どうやらブロックは、Hash を返す必要がありそう。
確かに、ドキュメントには、new {|hash, key| ... } -> Hash
の記載がある。
1 | h = Hash.new {|hash,key| Hash.new} |
Hash.new を返してしまったので、ずっと空になってしまう。
若干予想と異なった動きをしたのはこちら。
1 | h = Hash.new {|hash,key| {a: "V"}} |
この動きは、h[:a]
のように値が無い場合、{a: "V"}
を返すという形になっているだけだった。
次の 場合、h[:a] を呼び出したとき、その値が、"Value"
だと定義しています。
設定をすると値が入ります。
1 | h = Hash.new {|hash,key| hash[key] = "Value" } |
次のようにすると 2 段目の呼び出しまで対応します。
1 |
|
次の作り方で、3 段目まで対応。
1 | h = Hash.new {|hash,key| hash[key] = Hash.new {|hash,key| hash[key] = Hash.new {} } } |
と、これ以上深くするなら、何度も多重に書くこととなります。
ここで問題のコードを再度見る。
1 | h = Hash.new { |hash,key| hash[key] = Hash.new(&hash.default_proc) } |
ここまでの確認をしたことで、理解が及ぶようになった。
1 | h = Hash.new { |hash,key| hash[key] = Hash.new(&hash.default_proc) } |
ということは、こういう書き換えもできる。
1 | p = Proc.new {|hash,key| p "AAA"; hash[key] = Hash.new &hash.default_proc } |
とここまで来て完全に納得。
日常的に、毎回 Proc オブジェクトを分離する理由も無いので普段書くならこちらで良さそう。
1 | h = Hash.new { |hash, key| hash[key] = Hash.new(&hash.default_proc) } |
で、それを使って何をしたかったのか?
こんなテーブルが有ったとき。
ユーザー ID | 学年 | クラス | 教科 | 点数 |
---|---|---|---|---|
1000001 | 1 | A | Math | 20 |
1000002 | 1 | A | Math | 40 |
1000001 | 1 | A | Science | 20 |
1000002 | 1 | A | Science | 50 |
1000003 | 1 | B | Science | 20 |
1000004 | 2 | A | Math | 40 |
学年、クラス、教科で group 化するとこんなデータができる。
1 | [[[1,A,Math],30], [[1,A,Science],35], [[1,B,Science],20], [[2,A,Math],40]] |
これをハッシュで表現したいので、多段ハッシュを作れるようにしておくと次の方法で処理できる。
1 | src = [[[1,"A","Math"],30], [[1,"A","Science"],35], [[1,"B","Science"],20], [[2,"A","Math"],40]] |
多段のハッシュをまとめて作れないと、各階層のキーが存在するか全て確認することになってしまう。
さらに、active_support を導入しておくと、文字列とシンボルどちらでも対応できる。
1 | require 'active_support' |
というわけで、多段階のハッシュを1回で作成をする方法を書いた記事を見つけたが、コピペはいやなのでいろいろ調べてみた。
理解が追いついたので、ぜひ使っていきたい。
ではでは。