Rubyをハッシュする配列


192

わかりましたので、ここで取り引きを説明します。私はこれを解決するために長い間探し続けてきました。そこにはたくさんありますが、彼らは私が探している仕事をしていないようです。

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

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

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

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

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

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

回答:


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

それでおしまい。*呼ばれるスプラット演算子。

@Mike Lewisによる1つの警告(コメント内):「これには十分注意してください。Rubyはスタック上のスプラットを拡張します。これを大きなデータセットで実行する場合は、スタックを一掃してください。」

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

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

10
@tester、*呼び出されたスプラットの演算子。これは配列を取り、それを項目のリテラルリストに変換します。つまり*[1,2,3,4]=> 1, 2, 3, 4です。この例では、上記はを実行することと同じHash["item 1", "item 2", "item 3", "item 4"]です。そしてHash持っている[](でも、インデックスキーと奇数のインデックス値を作る)引数のリストを受け取るメソッドを、しかし、Hash[]我々が使用してアレイをスプラットので、配列を受け付けません*
ベン・リー

15
これには十分注意してください。Rubyはスタック上のスプラットを拡張します。大きなデータセットを使用してこれを行う場合は、スタックを一掃することを期待してください。
マイクルイス

9
ビッグデータテーブルで使用できますHash[a.each_slice(2).to_a]
Hauleth 2013

4
「スタックを吹き消す」とはどういう意味ですか?
ケビン

6
@Kevin、スタックは、プログラムが特定の特定の操作のために割り当て、予約する小さなメモリ領域を使用します。最も一般的には、これまでに呼び出されたメソッドのスタックを保持するために使用されます。これがスタックトレースという用語の起源であり、無限再帰メソッドがスタックオーバーフローを引き起こす可能性があるのもこのためです。この回答のメソッドもスタックを使用しますが、スタックはメモリの小さな領域であるため、このメソッドを大きな配列で試すと、スタックがいっぱいになり、エラー(同じ行に沿ったエラー)が発生しますスタックオーバーフロー)。
ベン・リー

103

Ruby 2.1.0では、to_h元の配列がキーと値のペアの配列で構成されている場合に必要な処理を行うメソッドがArrayに導入されました。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}

1
綺麗な!ここにある他のソリューションよりもはるかに優れています。
Dennis

3
2.1.0より前のrubyバージョンの場合、ネストされた配列のペアがある限り、Hash :: []メソッドを使用して同様の結果を得ることができます。つまり、a = [[:foo、:1]、[bar、2]] --- Hash [a] => {:foo => 1、:bar => 2}
AfDev

@AfDev、本当にありがとう。あなたは正しいです(マイナーなタイプミスを無視する場合:barシンボルである:2必要があり、シンボルは整数である必要があります。したがって、修正された式はですa = [[:foo, 1], [:bar, 2]])。
Jochem Schulenklopper 2014

28

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

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

1
[* arr]はどういう意味ですか?
Alan Coromano 2013

1
@Marius:引数リストに*arr変換arrするため[]、arrの内容を引数としてHash のメソッドを呼び出します。
チャック

26

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

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

2
あなたの答えは質問とは関係なく、あなたの場合はそれを使う方がずっと簡単ですHash[*arr]
Yossi

2
いいえ。戻るでしょう{ [1, 2] => [3, 4] }。また、質問のタイトルには「Array to Hash」とあり、組み込みの「Hash to Array」メソッドでは次の{ 1 => 2, 3 => 4}.to_a # => [[1, 2], [3, 4]]ようになっているので、組み込みの「Hash to Array」メソッドの逆を取得しようとして、ここで複数の方法で終了できると思いました。実際、それが私がとにかくここで終わった方法です。
エリックエスコベド

1
すみません、予備のアスタリスクを追加しました。Hash[arr]あなたのために仕事をします。
Yossi

9
IMHOのより良い解決策:Hash [* array.flatten(1)]
ゲスト

2
Yossi:死者を出してごめんね。でも彼の答えにはもう1つ大きな問題があります#inject。それはメソッドの使用です。で#merge!#each_with_object使用されている必要があります。場合は#inject、を主張される#mergeのではなく、#merge!使用されている必要があります。
Boris Stitnicky 2013年

12

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

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


あなたは使いたくないmerge、それはループごとの反復ごとに新しいハッシュを構築して破棄し、非常に遅いです。ハッシュの配列がある場合は、[{a:1},{b:2}].reduce({}, :merge!)代わりに試してください。すべてのハッシュが同じ(新しい)ハッシュにマージされます。

ありがとう、これも私が欲しかったものです!:)
Thanasis Petsas 2016

あなたも行うことができます.reduce(&:merge!)
ベン・リー

1
[{a: 1}, {b: 2}].reduce(&:merge!)評価された{:a=>1, :b=>2}
ベン・リー

これは、インジェクト/リデュースに引数を省略できる機能があるため機能します。この場合、配列の最初の引数を入力引数として操作し、残りの配列を配列として使用します。これをsymbol-to-procと組み合わせると、この簡潔な構造になります。換言すれば[{a: 1}, {b: 2}].reduce(&:merge!)同じである[{a: 1}, {b: 2}].reduce { |m, x| m.merge(x) }と同じです[{b: 2}].reduce({a: 1}) { |m, x| m.merge(x) }
ベン・リー・

10

Enumerator含まれていEnumerableます。なので2.1Enumerableメソッドもあり#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、上記の説明に従って#to_hEnumeratorオブジェクトのメソッドを呼び出すことができます。


7

単一の配列の場合、このように試すことができます

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
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"

あなたは完全には嫌いではありません場合はHash[ ... ]なく、(あなたが行うことができますように連鎖する方法としてそれを使用したいto_h)あなたはボリスの提案や書き込みを組み合わせることができます:arr.each_slice( 2 ).map { |e| e }.tap { |a| break Hash[a] }
B-スタジオ

上記のコードのセマンティクスをより明確にするために:これは、「空のハッシュ」hを作成します。これは最初は空で、必要に応じて元の配列aから要素を引き出します。そうして初めて、実際にhに保存されます!
Daniel Werner

1

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

を見ようよ:

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

item 1 => item 2上書きされるため、ペアは失われますitem 1 => item 5

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

を含むすべてのメソッド reduce(&:merge!)同じ削除の結果。

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

{"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"]}

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.