LoginSignup
21
17

More than 5 years have passed since last update.

RubyのHash.newにハマった話

Last updated at Posted at 2014-12-11

RubyのHashは、値をセットしていないkeyの値を取得しようとするとnil が返るが、 Hash.new(x)を使うことでデフォルト値を任意の値に変更できる。

a = {}
p a     #=> {}
p a[:x] #=> nil

b = Hash.new(0)
p b     #=> {}
p b[:h] #=> 0
b[:i] += 1
b[:j] += 2
p b     #=> {:i => 1, :j => 2}

しかし、デフォルト値を破壊的に変更してしまうと、予測とは違う動きをするので注意しなければいけない。

x = Hash.new([])
p x  #=> {}
x[:p] << 1
x[:q] << 2
p x  #=> {}        え?????

x[:r] =x[:r] << 3
p x  #=> {:r=>[1, 2, 3]}      ?!?!?!?

x[:s] = x[:s]
p x  #=>  {:r=>[1, 2, 3], :s=>[1, 2, 3]}     !!?!!??!!???

Hashのデフォルト値を空にして、特定のkeyにどんどん値を追加していきたい、ということはよくありそうだ。
そういう場合、newのブロックを使い以下のようにするといい。

h = Hash.new{[]}

h[:x] += [1]
h[:x] += [2]
h[:y] += [3]
h[:y] += [4]

p h  #=> {:x=>[1, 2], :y=>[3, 4]}

Arrayオブジェクトを毎回新しく作るのはパフォーマンスがーー! と思うなら以下のようになるといいのかもしれない。

h = Hash.new{|hash, key| hash[key] = []}

h[:x] << 10
h[:x] << 20
h[:y] << 30
h[:y] << 40

p h #=> {:x=>[10, 20], :y=>[30, 40]}

最初、Rubyのバグか? と思ったけど、普通に仕様らしい。
ちゃんと読んだことなかったけど、リファレンスに載ってた。

singleton method Hash.new - Ruby 2.1.0 リファレンスマニュアル

21
17
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
17