web-dev-qa-db-ja.com

ハッシュする配列Ruby

わかりましたので、ここに契約があります、私はこれに対する解決策を見つけるために長年グーグルで探してきましたが、そこには多くのものがありますが、彼らは私が探している仕事をしていないようです。

基本的に私はこのような構造の配列を持っています

["item 1", "item 2", "item 3", "item 4"] 

これをハッシュに変換して、次のようにします

{ "item 1" => "item 2", "item 3" => "item 4" }

つまり、「偶数」インデックスにあるアイテムがキーであり、「奇数」インデックスにあるアイテムが値です。

これをきれいに行う方法はありますか?総当たり方式では、すべての偶数インデックスを個別の配列に引き出し、それらをループして値を追加するだけだと思います。

181
djhworld
a = ["item 1", "item 2", "item 3", "item 4"]
h = Hash[*a] # => { "item 1" => "item 2", "item 3" => "item 4" }

それでおしまい。 *splat演算子と呼ばれます。

@Mike Lewisごとに1つの警告(コメント):「これに非常に注意してください。Rubyはスタック上のスプラットを展開します。これを大規模なデータセットで行う場合は、スタックを吹き飛ばしてください。」

そのため、ほとんどの一般的な使用例では、この方法は優れていますが、大量のデータを変換する場合は別の方法を使用してください。たとえば、@ŁukaszNiemier(これもコメントにあります)は、大規模なデータセットに対してこのメ​​ソッドを提供します。

h = Hash[a.each_slice(2).to_a]
344
Ben Lee

Ruby 2.1.0では、配列にto_hメソッドが導入されました。これは、元の配列がキーと値のペアの配列で構成される場合に必要なことを行います。 http://www.Ruby-doc.org/core-2.1.0/ Array.html#method-i-to_h

[[:foo, :bar], [1, 2]].to_h
# => {:foo => :bar, 1 => 2}
94

配列の値にHash.[]を使用するだけです。例えば:

arr = [1,2,3,4]
Hash[*arr] #=> gives {1 => 2, 3 => 4}
27
Chuck

または、[key, value]配列の配列がある場合は、次のことができます。

[[1, 2], [3, 4]].inject({}) do |r, s|
  r.merge!({s[0] => s[1]})
end # => { 1 => 2, 3 => 4 }
25
Erik Escobedo

これは私がこれをグーグルで探しているときに探していたものです:

[{a: 1}, {b: 2}].reduce({}) { |h, v| h.merge v } => {:a=>1, :b=>2}

10
Karl Glaser

Enumerator には Enumerable が含まれます。 2.1なので、Enumerableにもメソッド #to_h があります。そのため、次のように記述できます。

a = ["item 1", "item 2", "item 3", "item 4"]
a.each_slice(2).to_h
# => {"item 1"=>"item 2", "item 3"=>"item 4"}

#each_slice ブロックなしではEnumeratorが得られるため、上記の説明に従って、Enumeratorオブジェクトで#to_hメソッドを呼び出すことができます。

7
Arup Rakshit

単一の配列に対して、このように試すことができます

irb(main):019:0> a = ["item 1", "item 2", "item 3", "item 4"]
  => ["item 1", "item 2", "item 3", "item 4"]
irb(main):020:0> Hash[*a]
  => {"item 1"=>"item 2", "item 3"=>"item 4"}

配列の配列

irb(main):022:0> a = [[1, 2], [3, 4]]
  => [[1, 2], [3, 4]]
irb(main):023:0> Hash[*a.flatten]
  => {1=>2, 3=>4}
6
Jenorish
a = ["item 1", "item 2", "item 3", "item 4"]
Hash[ a.each_slice( 2 ).map { |e| e } ]

または、Hash[ ... ]が嫌いな場合:

a.each_slice( 2 ).each_with_object Hash.new do |(k, v), h| h[k] = v end

または、壊れた関数型プログラミングの怠け者の場合:

h = a.lazy.each_slice( 2 ).tap { |a|
  break Hash.new { |h, k| h[k] = a.find { |e, _| e == k }[1] }
}
#=> {}
h["item 1"] #=> "item 2"
h["item 3"] #=> "item 4"
5
Boris Stitnicky

すべての回答は、開始配列が一意であることを前提としています。 OPは、重複したエントリを持つ配列を処理する方法を指定しませんでした。これにより、キーが重複します。

見てみましょう:

a = ["item 1", "item 2", "item 3", "item 4", "item 1", "item 5"]

Bij item 1 => item 2がオーバーライドされると、item 1 => item 5ペアは失われます。

Hash[*a]
=> {"item 1"=>"item 5", "item 3"=>"item 4"}

reduce(&:merge!)を含むすべてのメソッドは、同じ削除をもたらします。

ただし、これはまさにあなたが期待していることかもしれません。しかし、他の場合では、代わりにArrayの値を使用して結果を取得する必要があります。

{"item 1"=>["item 2", "item 5"], "item 3"=>["item 4"]}

単純な方法は、ヘルパー変数、デフォルト値を持つハッシュを作成し、それをループで埋めることです:

result = Hash.new {|hash, k| hash[k] = [] } # Hash.new with block defines unique defaults.
a.each_slice(2) {|k,v| result[k] << v }
a
=> {"item 1"=>["item 2", "item 5"], "item 3"=>["item 4"]}

assocreduceを使用して上記の1行を実行することは可能かもしれませんが、それを推論して読むことははるかに難しくなります。

0
berkes