web-dev-qa-db-ja.com

2要素配列の配列をハッシュに変換します。重複キーは追加の値を追加します

例えば

与えられた配列:

array = [[:a,:b],[:a,:c],[:c,:b]]

次のハッシュを返します。

hash = { :a => [:b,:c] , :c => [:b] }

hash = Hash[array]は以前の関連付けを上書きし、以下を生成します:

hash = { :a => :c , :c => :b }
35
smallsense

機能的なベビーステップの使用:

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}
77
Phrogz

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は配列内の現在のオブジェクトです

  • メモは、ブロックの最後の反復によって返された値です(最初の反復の場合、これは注入したものです)

29
Abdo

これは、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]}
1
Ian