Hash ハッシュ
- いわゆる連想配列
- 1.9からハッシュに含まれる要素の順番が追加された順序で保持されるようになった
ハッシュの生成
ハッシュのキーには任意のオブジェクトが使えるが、 シンボルがよく使われる。
リテラル
旧記法
{:a => 1}
=> {:a=>1}
{:a => 1}.class
=> Hash
1.9からの新記法
{a: 1}
=> {:a=>1}
{a: 1}.class
=> Hash
2.2からの新記法(現在のRuby資格試験の対象はVer2.1の為、この書き方は出来ない、という考えで臨むことになる)
{'a': 1, 'b-c': 2}
=> {:a=>1, :"b-c"=>2}
{'a': 1, 'b-c': 2}.class
=> Hash
Hash.[] メソッド
この方法は積極的に使う理由がない限り使う必要は無いでしょう
Hash[1, 2, 3, 4]
=> {1=>2, 3=>4}
Hash.new メソッド
newメソッドを使用した場合、キーが存在しない場合のデフォルト値を設定できます がぶっちゃけ使いません
h = Hash.new('test')
h.class
=> Hash
h[:hage]
=> '"test"'
配列からハッシュへ変換
Hash[[[:c, 1], [:a, 2], [:b, 3]]]
=> {:c=>1, :a=>2, :b=>3}
[[:c, 1], [:a, 2], [:b, 3]].to_h
=> {:c=>1, :a=>2, :b=>3}
初期値はハッシュ作成後でも変更できる
h = Hash.new('test')
h.default
=> "test"
h = {}
h[:hage]
=> nil
h.default
=> nil
h.default = 'test'
h.default
=> 'test'
h[:hage]
=> 'test'
- newメソッドはブロックを取ることができる
- このブロックは新規キーを読み取り参照した時に処理される
h = Hash.new do |hash, key|
p '= access new key'
p hash, key
hash[key] = (key == :hage1 ? 'a' : 'b')
end
h.class
=> Hash
h[:hage1]
"= access new key"
{}
:hage1
=> "a"
h[:hage1]
=> "a"
h[:hage2]
"= access new key"
{:hage1=>"a"}
:hage2
=> "b"
h[:hage2]
=> "b"
h[:hage3]
"= access new key"
{:hage1=>"a", :hage2=>"b"}
:hage3
=> "b"
h[:hage3]
=> "b"
参照ではなく、キーと値の新規設定時には動作しない。動作する必要もないし
h = Hash.new do |hash, key|
p '= access new key'
p hash, key
hash[key] = (key == :hage1 ? 'a' : 'b')
end
h.class
=> Hash
h[:hage1] = 'hage-!'
=> "hage-!"
- この初期値設定ブロックは Hash#default_proc で後から参照できる
- まぁいじる事は無いと思うけど・・
h = Hash.new do |hash, key|
hash[key] = (key == :hage1 ? 'a' : 'b')
end
h.class
=> Hash
h.default_proc
=> #<Proc:0x007fcaa10af730@(irb):77>
Array#to_h
ぶっちゃけ使わないけど
[[:a, 1], ['b', 2]].to_h
=> {:a=>1, "b"=>2}
ハッシュのキーや値を参照する
[] |
keys |
values |
values_at |
fetch |
select |
[] メソッド
そのまんま
h = {a: 1}
h[:a]
=> 1
keys メソッド
ハッシュの全てのキーの配列を生成する
{a: 1, b: 2, c: 3, d: 4}.keys
=> [:a, :b, :c]
values メソッド
ハッシュの全ての値の配列を生成する
{a: 1, b: 2, c: 3, d: 4}.values
=> [1, 2, 3]
values_at メソッド
複数の引数を取り、指定されたキーに対応する値による配列を生成する
{a: 1, b: 2, c: 3, d: 4}.values_at(:a, :c)
=> [1, 3]
fetch メソッド
キーが存在する場合は値を返すのみ、下記以降の例でもそれは変わらない
{a: 1, b: 2, c: 3, d: 4}.fetch(:a)
=> 1
- キーが存在しない場合
- 引数がキーのみの場合、KeyError例外が発生
{a: 1, b: 2, c: 3, d: 4}.fetch(:z)
KeyError: key not found: :z
- キーが存在しない場合
- 引数が2つの場合、第2引数を返す
{a: 1, b: 2, c: 3, d: 4}.fetch(:z, 'test')
=> "test"
- キーが存在しない場合
- ブロックを取っている場合は、キーが存在しない場合にブロックの評価結果を返す
{a: 1, b: 2, c: 3, d: 4}.fetch(:z) do |key|
key.to_s
end
=> "z"
select メソッド
- キーと値の組み合わせにおいてブロックを評価し、結果がtrueになる結果のキーと値を返す
- 1.8以前は配列を返す
- 1.9以降はハッシュを返す
{a: 1, b: 2, c: 3, d: 4}.select do |key, value|
value % 2 == 0
end
=> {:b=>2, :d=>4}
ハッシュを変更する
[]= | ||
delete | ||
reject | reject! | delete_if |
replace | ||
shift | ||
merge | merge! | update |
invert | ||
clear |
[]= メソッド
- キーが存在しない場合には登録し値を設定
- キーが存在するなら値を変更
- まぁそのまんま
h = {}
h[:a]
=> nil
h[:a] = 'hage-!'
h[:a]
=> "hage-!"
delete メソッド
指定されたキーと値を取り除く
h = {a: 1, b: 2, c: 3, d: 4}
h
=> {:a=>1, :b=>2, :c=>3, :d=>4}
h.delete(:b)
=> 2
h
=> {:a=>1, :c=>3, :d=>4}
指定したキーが存在しなければ戻り値はnil
h = {a: 1, b: 2, c: 3, d: 4}
h
=> {:a=>1, :b=>2, :c=>3, :d=>4}
h.delete(:z)
=> nil
h
=> {:a=>1, :b=>2, :c=>3, :d=>4}
ブロックを取った場合、キーが存在しない場合にブロックの評価結果を返す
h = {a: 1, b: 2, c: 3, d: 4}
h
=> {:a=>1, :b=>2, :c=>3, :d=>4}
h.delete(:z) do |key|
p "Not found #{key}"
key
end
"Not found z"
=> :z
reject reject! delete_if メソッド
キーと値毎にブロックを評価し、結果がtrueとなったキーと値を取り除いたハッシュを生成する
{a: 1, b: 2, c: 3, d: 4}.reject do |key, value|
value % 2 == 0
end
=> {:a=>1, :c=>3}
reject!とdelete_ifは同じ動作となり、自己を破壊的に変更する
h = {a: 1, b: 2, c: 3, d: 4}
h.reject! do |key, value|
value % 2 == 0
end
=> {:a=>1, :c=>3}
h
=> {:a=>1, :c=>3}
replace メソッド
自分自身のオブジェクトIDを変えることなく、引数で指定されたハッシュで自分自身の内容を置き換えます
a = {a: 1, b: 2}
b = {c: 3, d: 4}
a
=> {:a=>1, :b=>2}
a.object_id
=> 70254130865000
a.replace(b)
=> {:c=>3, :d=>4}
a
=> {:c=>3, :d=>4}
a.object_id
=> 70254130865000
shift メソッド
- キーと値のペアを1つ取り除き、それを返す
- どのキーと値のペアが取り除かれるかは不定・・とこの本には書いてあるが、追加した順に取得できているとしか思えないがどうなのだろう
h = {}
h[:a] = 1
h[:b] = 2
h[:c] = 3
h.shift
=> [:a, 1]
h.shift
=> [:b, 2]
h.shift
=> [:c, 3]
merge merge! update メソッド
- 自分自身と引数で渡されたハッシュを統合した新たなハッシュオブジェクトを生成する
- 引数で渡されたハッシュのキーと値が優先される
- merge! と update は同じ動作、レシーバ破壊的動作となる
{a: 1, b: 1}.merge({a: 2, c: 2})
=> {:a=>2, :b=>1, :c=>2}
defaultは自分自身のものが引き継がれる
a = {}
b = {}
a.default = 'a'
b.default = 'b'
a.merge(b).default
=> "a"
- ブロックを取った場合、
- キーが被った場合、キー、自分自身の値、引数に指定されたハッシュの3つがブロックに渡され、その評価結果が値として設定される
a = {a: '1-a', b: '1-b', d: '1-d'}
b = {a: '2-a', b: '2-b', c: '2-c'}
a.merge(b) do |key, value1, value2|
p "Duplicate #{key}"
p key, value1, value2
value2
end
"Duplicate a"
:a
"1-a"
"2-a"
"Duplicate b"
:b
"1-b"
"2-b"
=> {:a=>"2-a", :b=>"2-b", :d=>"1-d", :c=>"2-c"}
invert メソッド
キーと値を逆にしたハッシュを返す
{a: 1, b: 2}.invert
=> {1=>:a, 2=>:b}
同じ値が存在していた場合、キーが被る為 結果は不定となる とこの本に書いてあるが、どう見ても後からハッシュに追加されたキーと値が優先される
h = {}
h[:a] = 1
h[:b] = 1
h[:c] = 1
h.invert
=> {1=>:c}
{a: 1, b: 1, c: 1}.invert
=> {1=>:c}
clear メソッド
全てのキーと値を削除する もちろん object_id は変わらない
a = {a: 1}
a.object_id
=> 70254131321800
a.clear
=> {}
a
=> {}
a.object_id
=> 70254131321800
ハッシュを調べる
lengrh | size | ||
empty? | |||
has_key? | incluce? | key? | member? |
has_value? | value? |
length size メソッド
キーと値のペアの数を返す
{a: 1, b: 2}.length
=> 2
empty?
空のハッシュの場合、trueを返す
{a: 1}.empty?
=> false
{}.empty?
=> true
has_key? incluce? key? member? メソッド
みんな同じ動作、キーが存在している場合はtrueを返す
{}.has_key?(:a)
=> false
{}.include?(:a)
=> false
{}.key?(:a)
=> false
{}.member?(:a)
=> false
{a: 1}.has_key?(:a)
=> true
{a: 1}.include?(:a)
=> true
{a: 1}.key?(:a)
=> true
{a: 1}.member?(:a)
=> true
has_value? value? メソッド
両方とも同じ動作、値が存在している場合はtrueを返す
{}.has_value?(1)
=> false
{}.value?(1)
=> false
{a: 1}.has_value?(1)
=> true
{a: 1}.value?(1)
=> true
ハッシュを使った繰り返し
each | each_pair |
each_key | |
each_value |
この書籍には 繰り返しの順番は不定である と書いてあるが、恐らく1.8の頃の文章をそのまま使っていると思われる。1.9からは順序が保持される事を考慮すべきかと思う
each each_pair メソッド
ブロックにキーと値のペアを繰り返し渡し、評価させる
{a: 1, b: 2, c: 3}.each do |key, value|
p key, value
end
:a
1
:b
2
:c
3
=> {:a=>1, :b=>2, :c=>3}
each_key メソッド
ブロックにキーを繰り返し渡し、評価させる
{a: 1, b: 2, c: 3}.each_key do |key|
p key
end
:a
:b
:c
=> {:a=>1, :b=>2, :c=>3}
each_value メソッド
ブロックに値を繰り返し渡し、評価させる
{a: 1, b: 2, c: 3}.each_value do |value|
p value
end
1
2
3
=> {:a=>1, :b=>2, :c=>3}
ハッシュをソートする
「Ruby 1.9では、Hashクラスのsortメソッドは廃止されました。Ruby 1.9ではEnumerableモジュールのsortメソッドを実行します」
キー優先、辞書順、そんな感じの動きをする
{b: 99, a: 1, c: 50, az: 10, ba:50}.sort
=> [[:a, 1], [:az, 10], [:b, 99], [:ba, 50], [:c, 50]]
戻り値が配列になることに注意
{b: 99, a: 1, c: 50, az: 10, ba:50}.sort
=> [[:a, 1], [:az, 10], [:b, 99], [:ba, 50], [:c, 50]]
{b: 99, a: 1, c: 50, az: 10, ba:50}.sort {|a, b| a[1] <=> b[1]}
=> [[:a, 1], [:az, 10], [:c, 50], [:ba, 50], [:b, 99]]
ハッシュに変換するにはこんな感じ
Hash[{b: 99, a: 1, c: 50, az: 10, ba:50}.sort]
=> {:a=>1, :az=>10, :b=>99, :ba=>50, :c=>50}
{b: 99, a: 1, c: 50, az: 10, ba:50}.sort {|a, b| a[1] <=> b[1]}.to_h
=> {:a=>1, :az=>10, :c=>50, :ba=>50, :b=>99}
メモ
キーワード引数 も併せて理解しておくべし
メソッド呼び出し時の実引数の{}波カッコの省略
- ぶっちゃけ使わないかな?
- 最後の引数だけ有効
def moko(a, b, c)
p a
p b
p c
end
moko(1, 2, a: 1, 'b' => 2)
1
2
{:a=>1, "b"=>2}
=> {:a=>1, "b"=>2}
# 引数の数的に考えて、残りの部分がハッシュとして解釈できそうなら、
moko(1, 2, { a: 1, 'b' => 2 })
# こう書いたかの如く動作するよ!って事だわな