与えられた配列:
array = [[:a,:b],[:a,:c],[:c,:b]]
次のハッシュを返します。
hash = { :a => [:b,:c] , :c => [:b] }
hash = Hash[array]
は以前の関連付けを上書きし、以下を生成します:
hash = { :a => :c , :c => :b }
機能的なベビーステップの使用:
irb:01.0> array = [[:a,:b],[:a,:c],[:c,:b]]
#=> [[:a, :b], [:a, :c], [:c, :b]]
irb:02.0> array.group_by(&:first)
#=> {:a=>[[:a, :b], [:a, :c]], :c=>[[:c, :b]]}
irb:03.0> array.group_by(&:first).map{ |k,a| [k,a.map(&:last)] }
#=> [[:a, [:b, :c]], [:c, [:b]]]
irb:04.0> Hash[ array.group_by(&:first).map{ |k,a| [k,a.map(&:last)] } ]
#=> {:a=>[:b, :c], :c=>[:b]}
命令型プログラミングを使用する:
irb:10.0> h = Hash.new{ |h,k| h[k]=[] }
#=> {}
irb:11.0> array.each{ |k,v| h[k] << v }
#=> [[:a, :b], [:a, :c], [:c, :b]]
irb:12.0> h
#=> {:a=>[:b, :c], :c=>[:b]}
必須のワンライナーとして:
irb:13.0> h = Hash.new{ |h,k| h[k]=[] }.tap{ |h| array.each{ |k,v| h[k] << v } }
#=> {:a=>[:b, :c], :c=>[:b]}
または、みんなのお気に入りのinject
を使う:
irb:14.0> array.inject(Hash.new{ |h,k| h[k]=[] }){ |h,(k,v)| h[k] << v; h }
#=> {:a=>[:b, :c], :c=>[:b]}
配列として衝突しない単一の値が本当に必要な場合は、後処理ステップとしてそれらの配列を解除するか、衝突時にのみ配列を作成する別のハッシュ累積戦略を使用できます。または、これを頭にかぶります:
irb:17.0> hashes = array.map{ |pair| Hash[*pair] } # merge many mini hashes
#=> [{:a=>:b}, {:a=>:c}, {:c=>:b}]
irb:18.0> hashes.inject{ |h1,h2| h1.merge(h2){ |*a| a[1,2] } }
#=> {:a=>[:b, :c], :c=>:b}
EDIT:Ruby 2.1+では、 Array#to_h を使用できます
pry(main)> [[:a,:b],[:a,:c],[:c,:b]].to_h
=> {:a=>:c, :c=>:b}
END EDIT
Hashクラスのpublic []メソッドは、キーと値のペアの配列を受け入れ、配列の最初の要素をキー、2番目の要素を値とするハッシュを返します。
キーと値のペアの最後の値は、キーの重複がある場合の実際の値になります。
Hash[[[:a,:b],[:a,:c],[:c,:b]]]
=> {:a=>:c, :c=>:b}
この構文は1.9.3以降で有効です。以前のバージョンがわからないRubyバージョン(1.8.7では無効)
ref: http://www.Ruby-doc.org/core-2.1.0/Hash.html#method-c-5B-5D
これを実行するもう1つの興味深い方法は、injectメソッドを使用することです(明らかに、上記の方法はより簡潔で、この特定の問題に推奨されます)。
[ [:a, :b], [:a, :c], [:c, :b] ].inject({}) { |memo, obj|
memo[obj.first] = obj.last
memo
}
=> {:a=>:c, :c=>:b}
injectは、列挙されたパラメータ(この場合は配列)を反復します。注入されたパラメータ(この場合は空のハッシュ{})から始まります。
列挙可能なオブジェクトごとに、変数memoとobjを使用してブロックが呼び出されます。
objは配列内の現在のオブジェクトです
メモは、ブロックの最後の反復によって返された値です(最初の反復の場合、これは注入したものです)
これは、each_with_object
を使用してかなり簡潔に行うことができます。
array.each_with_object({}) { |(k, v), h| h[k] = (h[k] || []) + [v] }
irb
のデモ:
irb(main):002:0> array = [[:a,:b],[:a,:c],[:c,:b]]
=> [[:a, :b], [:a, :c], [:c, :b]]
irb(main):003:0> array.each_with_object({}) { |(k, v), h| h[k] = (h[k] || []) + [v] }
=> {:a=>[:b, :c], :c=>[:b]}