Ruby Language
ハッシュ
サーチ…
前書き
ハッシュは、ユニークなキーとその値の辞書のようなコレクションです。連想配列とも呼ばれ、配列に似ていますが、配列が整数としてインデックスを使用する場合、Hashでは任意のオブジェクト型を使用できます。そのキーを参照してハッシュで新しいエントリを取得または作成します。
構文
{first_name: "Noel"、second_name: "Edmonds"}
{:first_name => "Noel"、:second_name => "Edmonds"}
{"ファーストネーム" => "ノエル"、 "セカンドネーム" => "エドモンズ"}
{first_key => first_value、second_key => second_value}
備考
ハッシュテーブルを使用して、Rubyのハッシュを値にマップします。
任意のハッシュ可能オブジェクトをキーとして使用できます。しかし、 Symbol
を使用するのは非常に一般的です。なぜなら、オブジェクトの割り当てが減っているため、いくつかのRubyバージョンではより効率的です。
{ key1: "foo", key2: "baz" }
ハッシュの作成
Rubyのハッシュは、キーを値にマッピングするハッシュテーブルを実装するオブジェクトです。 Rubyは、 {}
を使ってハッシュを定義するための特定のリテラル構文をサポートしています:
my_hash = {} # an empty hash
grades = { 'Mark' => 15, 'Jimmy' => 10, 'Jack' => 10 }
ハッシュは、標準的new
メソッドを使用して作成することもできます。
my_hash = Hash.new # any empty hash
my_hash = {} # any empty hash
ハッシュは、配列、オブジェクト、その他のハッシュのような複雑な型を含むあらゆる型の値を持つことができます:
mapping = { 'Mark' => 15, 'Jimmy' => [3,4], 'Nika' => {'a' => 3, 'b' => 5} }
mapping['Mark'] # => 15
mapping['Jimmy'] # => [3, 4]
mapping['Nika'] # => {"a"=>3, "b"=>5}
また、複雑なものも含め、どのような種類のキーでも構いません:
mapping = { 'Mark' => 15, 5 => 10, [1, 2] => 9 }
mapping['Mark'] # => 15
mapping[[1, 2]] # => 9
シンボルは一般にハッシュキーとして使用され、Ruby 1.9はこのプロセスを短縮するための特別な構文を導入しました。次のハッシュは同等です:
# Valid on all Ruby versions
grades = { :Mark => 15, :Jimmy => 10, :Jack => 10 }
# Valid in Ruby version 1.9+
grades = { Mark: 15, Jimmy: 10, Jack: 10 }
すべてのキーが文字列であるため、次のハッシュ(すべてのRubyバージョンで有効)は異なります。
grades = { "Mark" => 15, "Jimmy" => 10, "Jack" => 10 }
両方の構文バージョンを混在させることができますが、以下は推奨されません。
mapping = { :length => 45, width: 10 }
Ruby 2.2以降では、シンボルキーでハッシュを作成するための代替構文があります(シンボルにスペースが含まれている場合に最も便利です)。
grades = { "Jimmy Choo": 10, :"Jack Sparrow": 10 }
# => { :"Jimmy Choo" => 10, :"Jack Sparrow" => 10}
値へのアクセス
[]
と[]=
メソッドを使用して、ハッシュの個々の値を読み書きします。
my_hash = { length: 4, width: 5 }
my_hash[:length] #=> => 4
my_hash[:height] = 9
my_hash #=> {:length => 4, :width => 5, :height => 9 }
デフォルトでは、ハッシュに追加されていないキーにアクセスするとnil
が返されます。つまり、キーの値を検索することは常に安全です。
my_hash = {}
my_hash[:age] # => nil
ハッシュには、文字列にキーを含めることもできます。それらに通常アクセスしようとすると、単にnil
返されます。代わりに文字列キーでアクセスします:
my_hash = { "name" => "user" }
my_hash[:name] # => nil
my_hash["name"] # => user
キーが存在する必要がある、または存在する必要がある状況の場合、ハッシュにはfetch
メソッドがあります。これは、存在しないキーにアクセスするときに例外を発生させます。
my_hash = {}
my_hash.fetch(:age) #=> KeyError: key not found: :age
fetch
は、キーが以前に設定されていない場合に返される第2引数としてデフォルト値を受け取ります。
my_hash = {}
my_hash.fetch(:age, 45) #=> => 45
fetch
は、キーが以前に設定されていない場合に返されるブロックを受け入れることもできます:
my_hash = {}
my_hash.fetch(:age) { 21 } #=> 21
my_hash.fetch(:age) do |k|
puts "Could not find #{k}"
end
#=> Could not find age
ハッシュは[]=
エイリアスとしてstore
メソッドもサポートしています:
my_hash = {}
my_hash.store(:age, 45)
my_hash #=> { :age => 45 }
values
メソッドを使用して、ハッシュのすべての値を取得することもできvalues
。
my_hash = { length: 4, width: 5 }
my_hash.values #=> [4, 5]
注:これはRuby 2.3以降の #dig
のみです 。 #dig
はネストしたHash
便利です。 idxオブジェクトのシーケンスで指定されたネストされた値を各ステップでdigを呼び出し、中間ステップがnilの場合はnilを返すことによって抽出します。
h = { foo: {bar: {baz: 1}}}
h.dig(:foo, :bar, :baz) # => 1
h.dig(:foo, :zot, :xyz) # => nil
g = { foo: [10, 11, 12] }
g.dig(:foo, 1) # => 11
デフォルト値の設定
デフォルトでは、存在しないキーの値を参照しようとするとnil
が返されます。ハッシュが存在しないキーでアクセスされたときに返す値(または取るべきアクション)を指定することもできます。これは「デフォルト値」と呼ばれますが、単一の値である必要はありません。例えば、キーの長さなどの計算値であってもよい。
ハッシュのデフォルト値はそのコンストラクタに渡すことができます:
h = Hash.new(0)
h[:hi] = 1
puts h[:hi] # => 1
puts h[:bye] # => 0 returns default value instead of nil
すでに構築されているHashにもデフォルトを指定することができます:
my_hash = { human: 2, animal: 1 }
my_hash.default = 0
my_hash[:plant] # => 0
新しいキーにアクセスするたびにデフォルト値がコピーされないことに注意することが重要です 。これは、デフォルト値が参照タイプの場合に驚くべき結果につながる可能性があります。
# Use an empty array as the default value
authors = Hash.new([])
# Append a book title
authors[:homer] << 'The Odyssey'
# All new keys map to a reference to the same array:
authors[:plato] # => ['The Odyssey']
この問題を回避するために、Hashコンストラクタは、新しいキーにアクセスするたびに実行されるブロックを受け入れ、その戻り値がデフォルトとして使用されます。
authors = Hash.new { [] }
# Note that we're using += instead of <<, see below
authors[:homer] += ['The Odyssey']
authors[:plato] # => []
authors # => {:homer=>["The Odyssey"]}
上記の例では、デフォルト値が自動的にハッシュに割り当てられないため、<<の代わりに+ =を使用する必要があります。 <<を使用すると配列に追加されますが、authors [:homer]は未定義のままです:
authors[:homer] << 'The Odyssey' # ['The Odyssey']
authors[:homer] # => []
authors # => {}
アクセス時にデフォルト値を割り当てたり、より洗練されたデフォルト値を計算したりできるようにするため、デフォルトブロックはハッシュとキーの両方に渡されます。
authors = Hash.new { |hash, key| hash[key] = [] }
authors[:homer] << 'The Odyssey'
authors[:plato] # => []
authors # => {:homer=>["The Odyssey"], :plato=>[]}
デフォルトのブロックを使用して、アクションを実行したり、キー(またはその他のデータ)に依存する値を返すこともできます。
chars = Hash.new { |hash,key| key.length }
chars[:test] # => 4
より複雑なハッシュを作成することもできます。
page_views = Hash.new { |hash, key| hash[key] = { count: 0, url: key } }
page_views["http://example.com"][:count] += 1
page_views # => {"http://example.com"=>{:count=>1, :url=>"http://example.com"}}
既存のハッシュのデフォルト値をProcに設定するには、 default_proc=
使用します。
authors = {}
authors.default_proc = proc { [] }
authors[:homer] += ['The Odyssey']
authors[:plato] # => []
authors # {:homer=>["The Odyssey"]}
ディープハッシュを自動的に作成する
ハッシュは、要求されたが存在しないキー(nil)のデフォルト値を持っています:
a = {}
p a[ :b ] # => nil
新しいHashを作成するときに、デフォルトを指定することができます:
b = Hash.new 'puppy'
p b[ :b ] # => 'puppy'
Hash.newもブロックをとります。これにより、Perlの自動化動作やmkdir -p
などのネストされたハッシュを自動的に作成できます。
# h is the hash you're creating, and k the key.
#
hash = Hash.new { |h, k| h[k] = Hash.new &h.default_proc }
hash[ :a ][ :b ][ :c ] = 3
p hash # => { a: { b: { c: 3 } } }
キーと値の変更
キーや値を変更して新しいハッシュを作成することもできます。実際には、 注射 (AKA、 reduce )を使用してキーを追加または削除することもできます。たとえば、文字列化されたキーと大文字の値を持つハッシュを生成するには:
fruit = { name: 'apple', color: 'green', shape: 'round' }
# => {:name=>"apple", :color=>"green", :shape=>"round"}
new_fruit = fruit.inject({}) { |memo, (k,v)| memo[k.to_s] = v.upcase; memo }
# => new_fruit is {"name"=>"APPLE", "color"=>"GREEN", "shape"=>"ROUND"}
ハッシュは列挙型であり、本質的にはキーと値のペアの集合です。したがって、 each
メソッド、 map
、 inject
などがあります。
ハッシュのすべてのキー/値のペアについて、与えられたブロックが評価されます。最初の実行時のメモの値は、 inject
ために渡されたシード値です。この場合、空のハッシュ{}
です。後続の評価のためのmemo
値は、前のブロック評価の戻り値です。これは、キーで値を設定してmemo
を変更し、最後にmemo
を返す理由です。最終ブロック評価の戻り値は、私たちのケースmemo
inject
の戻り値です。
最終的な値を提供する必要がないようにするには、代わりにeach_with_objectを使用します。
new_fruit = fruit.each_with_object({}) { |(k,v), memo| memo[k.to_s] = v.upcase }
またはマップする :
new_fruit = Hash[fruit.map{ |k,v| [k.to_s, v.upcase] }]
(ハッシュを適切に操作する方法など、詳細については、 この回答を参照してください)。
ハッシュを繰り返す
Hash
含まEnumerable
次のようないくつかの反復方法、提供モジュール、 Enumerable#each
、 Enumerable#each_pair
、 Enumerable#each_key
、およびEnumerable#each_value
。
.each
と.each_pair
は各キーと値のペアを繰り返します:
h = { "first_name" => "John", "last_name" => "Doe" }
h.each do |key, value|
puts "#{key} = #{value}"
end
# => first_name = John
# last_name = Doe
.each_key
はキーだけを反復します:
h = { "first_name" => "John", "last_name" => "Doe" }
h.each_key do |key|
puts key
end
# => first_name
# last_name
.each_value
は値だけを繰り返し処理します:
h = { "first_name" => "John", "last_name" => "Doe" }
h.each_value do |value|
puts value
end
# => John
# Doe
.each_with_index
は要素を反復処理し、反復のインデックスを提供します。
h = { "first_name" => "John", "last_name" => "Doe" }
h.each_with_index do |(key, value), index|
puts "index: #{index} | key: #{key} | value: #{value}"
end
# => index: 0 | key: first_name | value: John
# index: 1 | key: last_name | value: Doe
配列への変換と配列からの変換
ハッシュは配列間で自由に変換できます。キー/値のペアのハッシュを配列に変換すると、ペアのネストされた配列を含む配列が生成されます。
{ :a => 1, :b => 2 }.to_a # => [[:a, 1], [:b, 2]]
反対方向では、同じフォーマットの配列からハッシュを作成することができます:
[[:x, 3], [:y, 4]].to_h # => { :x => 3, :y => 4 }
同様に、ハッシュは、 Hash[]
と交互のキーと値のリストを使用して初期化することができます:
Hash[:a, 1, :b, 2] # => { :a => 1, :b => 2 }
または、それぞれ2つの値を持つ配列の配列から:
Hash[ [[:x, 3], [:y, 4]] ] # => { :x => 3, :y => 4 }
ハッシュは、 flatten()
を使用して、交互のキーと値の配列に変換することができます。
{ :a => 1, :b => 2 }.flatten # => [:a, 1, :b, 2]
配列への簡単な変換により、 Hash
はcollect
やzip
などの多くのEnumerable
メソッドでうまく動作しEnumerable
。
Hash[('a'..'z').collect{ |c| [c, c.upcase] }] # => { 'a' => 'A', 'b' => 'B', ... }
people = ['Alice', 'Bob', 'Eve']
height = [5.7, 6.0, 4.9]
Hash[people.zip(height)] # => { 'Alice' => 5.7, 'Bob' => '6.0', 'Eve' => 4.9 }
すべてのキーまたはハッシュ値の取得
{foo: 'bar', biz: 'baz'}.keys # => [:foo, :biz]
{foo: 'bar', biz: 'baz'}.values # => ["bar", "baz"]
{foo: 'bar', biz: 'baz'}.to_a # => [[:foo, "bar"], [:biz, "baz"]]
{foo: 'bar', biz: 'baz'}.each #<Enumerator: {:foo=>"bar", :biz=>"baz"}:each>
ハッシュ関数のオーバーライド
Rubyのハッシュはhash
とeql?
のメソッドを使用しますeql?
ハッシュ操作を実行し、ハッシュに格納されたオブジェクトを内部ハッシュ・ビンに割り当てることができます。 Rubyでのhash
のデフォルトの実装は、ハッシュされたオブジェクトのすべてのメンバーフィールドに対する 雑音ハッシュ関数 です 。この動作をオーバーライドするには、 hash
とeql?
をオーバーライドすることは可能eql?
メソッド。
他のハッシュ実装と同様に、2つのオブジェクトaとbは、 a.hash == b.hash
場合は同じバケットにハッシュされ、 a.eql?(b)
場合は同一とみなされます。したがって、 hash
とeql?
再実装するときはeql?
a
とb
がeql?
下で等しいかどうかを確認するために注意が必要eql?
同じhash
値を返す必要があります。そうしないと、ハッシュに重複したエントリが作成される可能性があります。逆に、 hash
実装の選択肢が悪いと、多くのオブジェクトが同じハッシュバケットを共有し、O(1)ルックアップ時間を効果的に破棄し、 eql?
を呼び出すためにO(n)を引き起こす可能性がありeql?
すべてのオブジェクトに適用されます。
次の例では、最初に追加されたクラスA
のインスタンスのみがキーとして格納されます。
class A
def initialize(hash_value)
@hash_value = hash_value
end
def hash
@hash_value # Return the value given externally
end
def eql?(b)
self.hash == b.hash
end
end
class B < A
end
a = A.new(1)
b = B.new(1)
h = {}
h[a] = 1
h[b] = 2
raise "error" unless h.size == 1
raise "error" unless h.include? b
raise "error" unless h.include? a
ハッシュをフィルタリングする
select
は、ブロックがtrue
評価するキーと値のペアを持つ新しいhash
を返しtrue
。
{ :a => 1, :b => 2, :c => 3 }.select { |k, v| k != :a && v.even? } # => { :b => 2 }
フィルタブロックでキーや値を必要としない場合は、その場所で_
を使用するのが一般的です。
{ :a => 1, :b => 2, :c => 3 }.select { |_, v| v.even? } # => { :b => 2 }
{ :a => 1, :b => 2, :c => 3 }.select { |k, _| k == :c } # => { :c => 3 }
reject
は、ブロックがfalse
評価するキーと値のペアを持つ新しいhash
を返します。
{ :a => 1, :b => 2, :c => 3 }.reject { |_, v| v.even? } # => { :a => 1, :c => 3 }
{ :a => 1, :b => 2, :c => 3 }.reject { |k, _| k == :b } # => { :a => 1, :c => 3 }
ハッシュに対する操作を設定する
ハッシュの交差点
2つのハッシュの共通部分を取得するには、値が等しい共有キーを返します。
hash1 = { :a => 1, :b => 2 } hash2 = { :b => 2, :c => 3 } hash1.select { |k, v| (hash2.include?(k) && hash2[k] == v) } # => { :b => 2 }
ハッシュの連合(マージ):
ハッシュのキーが一意である場合、マージする両方のハッシュでキーが発生すると、その
merge
が呼び出されたハッシュからのキーが上書きされます。hash1 = { :a => 1, :b => 2 } hash2 = { :b => 4, :c => 3 } hash1.merge(hash2) # => { :a => 1, :b => 4, :c => 3 } hash2.merge(hash1) # => { :b => 2, :c => 3, :a => 1 }