Rubyでハッシュ操作

配列の操作をまとめたので、ハッシュの操作もまとめておこうかなと思って書いてみた。基本的なデータ構造の扱いをマスターすることは日々のプログラミングのスピードアップに重要ですね。

リテラル

hash1 = { "key1" => "value1", "key2" => "value2" }
hash2 = { :key1 => "value1", :key2 => "value2" } # Railsでよく使われる書き方

# hash2は, Ruby1.9では以下のように記述できる(JavaScriptぽい)
hash2 = { key1: "value1", key2: "value2 }

Hashのキーにはどんなオブジェクトも使える。しかし格納するオブジェクトのクラスはeql?とhashメソッドをきちんと実装していないと結果的に使い物にならない可能性がある。Rubyの組み込みクラスの文字列、シンボルなどがキーとしてはよく利用されていると思われる。もちろん数値を使うこともできる。非常に疎な配列の代わりとしてHashを使うのはそういう意味では有効だろう。

値へのアクセス

値へのアクセスは配列と同じで[]を使う。Perlでは{}を使うので混同しないこと。複数のキーに対応する値の配列を取り出したいときはvalues_atを使う。

a = { "k1" => 10, "k2" => 20 }
p a["k1"] #=> 10
a["k1"] = 11
a["k2"] = 22

p a.values_at("k1", "k2")
my_keys = ["k1", "k2"]
p a.values_at(*my_keys) # a.values_at("k1", "k2")と同じ

ハッシュの大きさ

a = {"k1" => 10, "k2" => 20}
p a.size #=> 2
p a.length #=> 2 sizeの別名

後述するkeysを使ってa.keys.sizeでも正しい

キーの集合, 値の集合

a = {"k1" => 10, "k2" => 20}
p a.keys #=> ["k1", "k2"] 古いrubyでは順番は不定
p a.values #=> [10, 20] 古いrubyでは順番は不定
  • キーの集合(配列)を取り出すにはkeysを使う
  • 値の集合を取り出すにはa.valuesを使う

ハッシュの各キー/値ペアに対して処理を行いたい場合、a.keys.each{...などとして繰り返し処理を行ってもいいのだが、素直にHashクラスにeachといういてレータがあるのでそちらを使うのが楽である。

繰り返し

eachメソッドを使って内容を繰り返せる。

a = {"k1" => 10, "k2" => 20}
a.each do |key, value| # 新しいrubyでは代入した順にイテレートされる
  puts "key=#{key}, value=#{value}"
end

内容のクリア

clearメソッドを呼ぶとレシーバのハッシュの内容が空になる。

a = {"k1" => 10, "k2" => 20}
a.clear
p a #=> {}

フィルタリング

配列と同じようにselectとrejectが使える。delete_ifも。selectとrejectは非破壊的であり、条件に合致する新しいハッシュを生成して返す。

a = { :a => 1, :b => 2, :c => 3 }
p a.select{|k,v| v % 2 == 0} #=> 値が偶数のものを選択: {:b => 2}
p a.reject{|k,v| v % 2 == 0} #=> 値が偶数のものを排除: {:a => 1, :c => 3}
a.delete_if do |key, value|
  value % 2 == 0 # 偶数を削除してみる
end
p a #=> {:a => 1, :c => 3}

別のハッシュの内容をマージ

あるに、別のハッシュの内容を追加するには、mergeメソッドまたはmerge!メソッドを使います。ビックリマーク付きのメソッドはレシーバの変数の内容を書き換えますが、mergeメソッドはレシーバのハッシュと引数のハッシュをマージした新しいハッシュを返すのみで、レシーバは変更しません。

a = { :a => 1 }
b = { :b => 2 }
p a.merge(b) #=> { :a => 1, :b => 2 }
p a #=> #{ :a => 1 }
a.merge!(b)
p a #=> { :a => 1, :b => 2}

キーの存在確認

値がnilなキー/値のペアをセットした場合、デフォルトのHashインスタンスではキーに対応する値がない場合のデフォルト値がnilなので、[]メソッドの返り値がnilであるだけではキーが存在するかどうかが確認できないことがあります。そういう場合はkey?, has_key?, include?, member?の各メソッドを使います。どれも名前が違うだけで働きは一緒です。タイプ数が少ないのでkey?を使うと楽かもしれません。include?はArrayとの対応を考慮して存在しているのでしょうか。

a = { :a => 1, :b => 2 }
p a.key?(:a) #=> true
p a.key?(:b) #=> true
p a.key?(:c) #=> false