回答:
注:簡潔で効率的なソリューションについては、以下のMarc-AndréLafortuneの回答を参照してください。
この回答は、元々、執筆時点で最も高く支持されていたflattenを使用するアプローチの代替として提供されました。この例をベストプラクティスまたは効率的なアプローチとして提示するつもりはなかったことを明確にしておく必要がありました。元の答えは次のとおりです。
警告!フラット化を使用したソリューションをは、配列のキーまたは値を保持しません!
@John Topleyの人気の答えに基づいて、試してみましょう。
a3 = [ ['apple', 1], ['banana', 2], [['orange','seedless'], 3] ]
h3 = Hash[*a3.flatten]
これはエラーをスローします:
ArgumentError: odd number of arguments for Hash
from (irb):10:in `[]'
from (irb):10
コンストラクターは、偶数の長さの配列を期待していました(たとえば、['k1'、 'v1、' k2 '、' v2 '])。さらに悪いことに、偶数の長さにフラット化された別の配列は、誤った値のハッシュを静かに提供するだけです。
配列のキーまたは値を使用する場合は、mapを使用できます。
h3 = Hash[a3.map {|key, value| [key, value]}]
puts "h3: #{h3.inspect}"
これにより、配列キーが保持されます。
h3: {["orange", "seedless"]=>3, "apple"=>1, "banana"=>2}
h3 = Hash[*a3.flatten(1)]
代わりにh3 = Hash[*a3.flatten]
エラーがスローされます。
to_h
ほうがいいと思います。
単に使う Hash[*array_variable.flatten]
例えば:
a1 = ['apple', 1, 'banana', 2]
h1 = Hash[*a1.flatten(1)]
puts "h1: #{h1.inspect}"
a2 = [['apple', 1], ['banana', 2]]
h2 = Hash[*a2.flatten(1)]
puts "h2: #{h2.inspect}"
キーと値が期待どおりに機能するArray#flatten(1)
ように、再帰を制限して使用しますArray
。
Hash[*ary.flatten(1)]
、配列のキーと値を保持するを使用することもできます。flatten
それらを破壊しているのは再帰的であり、回避するのは簡単です。
最良の方法は、使用することArray#to_h
です:
[ [:apple,1],[:banana,2] ].to_h #=> {apple: 1, banana: 2}
注to_h
また、ブロックを受け付けます。
[:apple, :banana].to_h { |fruit| [fruit, "I like #{fruit}s"] }
# => {apple: "I like apples", banana: "I like bananas"}
注:to_h
Ruby 2.6.0以降のブロックを受け入れます。初期のルビーには、私のbackports
宝石を使用できますrequire 'backports/2.6.0/enumerable/to_h'
to_h
Ruby 2.1.0でブロックなしが導入されました。
Ruby 2.1以前は、読みにくいものを使用できましたHash[]
。
array = [ [:apple,1],[:banana,2] ]
Hash[ array ] #= > {:apple => 1, :banana => 2}
最後に、を使用した解決策に注意してくださいflatten
。これにより、配列自体の値で問題が発生する可能性があります。
to_h
に変換する意図を表すため、上記の回答よりも優れた方法が好きです。
Array#to_h
もコアルビーEnumerable#to_h
1.9にはありません。
[[apple, 1], [banana, 2], [apple, 3], [banana, 4]]
あり、出力が必要な場合はどうなり{"apple" =>[1,3], "banana"=>[2,4]}
ますか?
編集:私が書いている間に投稿された応答を見た、Hash [a.flatten]は進むべき道のようです。私が応答を通して考えていたとき、ドキュメントのそのビットを逃したに違いありません。私が書いたソリューションは、必要に応じて代替案として使用できると思いました。
2番目の形式はより単純です。
a = [[:apple, 1], [:banana, 2]]
h = a.inject({}) { |r, i| r[i.first] = i.last; r }
a =配列、h =ハッシュ、r =戻り値のハッシュ(蓄積されるもの)、i =配列内のアイテム
私が最初のフォームを実行するのを考えることができる最も最近の方法は次のようなものです:
a = [:apple, 1, :banana, 2]
h = {}
a.each_slice(2) { |i| h[i.first] = i.last }
a.inject({})
より柔軟な値の割り当てを可能にするワンライナーの+1 。
h = {}
最後にa.each_slice(2).inject({}) { |h,i| h[i.first] = i.last; h }
a.each_slice(2).to_h
この回答は、他の回答からの情報を包括的にまとめたものになることを期待しています。
質問のデータといくつかの追加を考慮した非常に短いバージョン:
flat_array = [ apple, 1, banana, 2 ] # count=4
nested_array = [ [apple, 1], [banana, 2] ] # count=2 of count=2 k,v arrays
incomplete_f = [ apple, 1, banana ] # count=3 - missing last value
incomplete_n = [ [apple, 1], [banana ] ] # count=2 of either k or k,v arrays
# there's one option for flat_array:
h1 = Hash[*flat_array] # => {apple=>1, banana=>2}
# two options for nested_array:
h2a = nested_array.to_h # since ruby 2.1.0 => {apple=>1, banana=>2}
h2b = Hash[nested_array] # => {apple=>1, banana=>2}
# ok if *only* the last value is missing:
h3 = Hash[incomplete_f.each_slice(2).to_a] # => {apple=>1, banana=>nil}
# always ok for k without v in nested array:
h4 = Hash[incomplete_n] # or .to_h => {apple=>1, banana=>nil}
# as one might expect:
h1 == h2a # => true
h1 == h2b # => true
h1 == h3 # => false
h3 == h4 # => true
議論と詳細は以下の通りです。
最初に使用するデータを示すために、データのさまざまな可能性を表す変数をいくつか作成します。これらは次のカテゴリに分類されます。
a1
およびa2
:(注:私はそれを想定してapple
おり、banana
変数を表すためのものでした。他の人が行ったように、入力と結果が一致するように、ここから文字列を使用します。)
a1 = [ 'apple', 1 , 'banana', 2 ] # flat input
a2 = [ ['apple', 1], ['banana', 2] ] # key/value paired input
a3
:他のいくつかの回答では、別の可能性が提示されました(ここで詳しく説明します)。キーまたは値、あるいはその両方が独自の配列である場合があります。
a3 = [ [ 'apple', 1 ],
[ 'banana', 2 ],
[ ['orange','seedless'], 3 ],
[ 'pear', [4, 5] ],
]
a4
:目安として、入力が不完全な場合に備えて1つ追加すると思います。
a4 = [ [ 'apple', 1],
[ 'banana', 2],
[ ['orange','seedless'], 3],
[ 'durian' ], # a spiky fruit pricks us: no value!
]
a1
ます。一部のユーザーは、使用を提案しています#to_h
(これはRuby 2.1.0で表示され、以前のバージョンにバックポートできます)。最初はフラットな配列の場合、これは機能しません。
a1.to_h # => TypeError: wrong element type String at 0 (expected array)
splatオペレーターHash::[]
と組み合わせて使用すると、次のことが行われます。
Hash[*a1] # => {"apple"=>1, "banana"=>2}
これが、で表される単純なケースの解決策ですa1
。
a2
次のようになります。配列と[key,value]
型の配列、行くには2つの方法があります。
まず、Hash::[]
まだ機能します(と同じように*a1
):
Hash[a2] # => {"apple"=>1, "banana"=>2}
そして#to_h
今も動作します:
a2.to_h # => {"apple"=>1, "banana"=>2}
したがって、単純なネストされた配列の場合の2つの簡単な答えです。
a3
。Hash[a3] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "pear"=>[4, 5]}
a3.to_h # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "pear"=>[4, 5]}
バランスが取れていない入力データを取得した場合、次の問題が発生し#to_h
ます。
a4.to_h # => ArgumentError: wrong array length at 3 (expected 2, was 1)
しかし、Hash::[]
それでも機能nil
し、値として設定するだけですdurian
(そして、1値の配列であるa4の他の配列要素):
Hash[a4] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "durian"=>nil}
a5
とa6
引数のflatten
有無にかかわらず、他のいくつかの回答が言及されている1
ので、新しい変数をいくつか作成してみましょう。
a5 = a4.flatten
# => ["apple", 1, "banana", 2, "orange", "seedless" , 3, "durian"]
a6 = a4.flatten(1)
# => ["apple", 1, "banana", 2, ["orange", "seedless"], 3, "durian"]
a4
で示されたバランスの問題があったため、ベースデータとして使用することにしましたa4.to_h
。呼び出しflatten
は、誰かがそれを解決するために使用する1つのアプローチであると考えられます。これは次のようになります。
flatten
引数なし(a5
):Hash[*a5] # => {"apple"=>1, "banana"=>2, "orange"=>"seedless", 3=>"durian"}
# (This is the same as calling `Hash[*a4.flatten]`.)
ナイーブ一見、仕事にこれが表示されますが-しかし、それはこのようにもなって、種なしオレンジと間違って足で私たちを降り、キーと値を。3
durian
そして、これはとa1
同様に機能しません:
a5.to_h # => TypeError: wrong element type String at 0 (expected array)
だからa4.flatten
私たちには役に立たない、ただ使用したいだけHash[a4]
flatten(1)
ケース(a6
):しかし、部分的にしか平坦化しないのはどうですか?部分的に平坦化された配列()をHash::[]
使用して呼び出すことは、を呼び出すことと同じではないことに注意してください。splat
a6
Hash[a4]
Hash[*a6] # => ArgumentError: odd number of arguments for Hash
a6
):しかし、これが最初に配列を取得する方法だったとしたらどうでしょうか。(つまり、と比較するとa1
、それは入力データでした。今回は一部のデータが配列または他のオブジェクトである可能性があります。)これHash[*a6]
は機能しないことがわかりましたが、最後の要素(重要!以下を参照)がnil
値のキーとして機能しましたか?
そのような状況でも、これを行う方法があり、外部配列の要素としてEnumerable#each_slice
キー/値のペアに戻るために使用します。
a7 = a6.each_slice(2).to_a
# => [["apple", 1], ["banana", 2], [["orange", "seedless"], 3], ["durian"]]
これにより、と「同一」ではないa4
が同じ値を持つ新しい配列が取得されることに注意してください。
a4.equal?(a7) # => false
a4 == a7 # => true
したがって、再び使用できますHash::[]
。
Hash[a7] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "durian"=>nil}
# or Hash[a6.each_slice(2).to_a]
最後のキーが値のないものであったeach_slice(2)
場合にのみ、解決策は物事を正気に戻すことに注意することが重要です。後でキーと値のペアを追加した場合:
a4_plus = a4.dup # just to have a new-but-related variable name
a4_plus.push(['lychee', 4])
# => [["apple", 1],
# ["banana", 2],
# [["orange", "seedless"], 3], # multi-value key
# ["durian"], # missing value
# ["lychee", 4]] # new well-formed item
a6_plus = a4_plus.flatten(1)
# => ["apple", 1, "banana", 2, ["orange", "seedless"], 3, "durian", "lychee", 4]
a7_plus = a6_plus.each_slice(2).to_a
# => [["apple", 1],
# ["banana", 2],
# [["orange", "seedless"], 3], # so far so good
# ["durian", "lychee"], # oops! key became value!
# [4]] # and we still have a key without a value
a4_plus == a7_plus # => false, unlike a4 == a7
そして、これから得られる2つのハッシュは、重要な点で異なります。
ap Hash[a4_plus] # prints:
{
"apple" => 1,
"banana" => 2,
[ "orange", "seedless" ] => 3,
"durian" => nil, # correct
"lychee" => 4 # correct
}
ap Hash[a7_plus] # prints:
{
"apple" => 1,
"banana" => 2,
[ "orange", "seedless" ] => 3,
"durian" => "lychee", # incorrect
4 => nil # incorrect
}
(注:私が使用しているawesome_print
のap
ちょうどそれが簡単にここ構造を示すために作るために、このための概念的な要件はありません。)
したがって、each_slice
不平衡フラット入力の解決策は、不平衡ビットが最後にある場合にのみ機能します。
[key, value]
ペア(外部配列の各項目のサブ配列)としてセットアップします。#to_h
またはHash::[]
両方が機能します。Hash::[]
は、splat(*
)と組み合わせると、入力のバランスが取れている限り機能します。value
項目が欠けている唯一のものです。補足:追加する価値があると感じたため、この回答を投稿しています。既存の回答の一部には不正確な情報があり、私がここでやろうとしているほど完全な回答はありません(私が読んだもの)。お役に立てれば幸いです。それでも、私の前に来てくれた人々に感謝します。そのうちのいくつかは、この答えの一部にインスピレーションを与えました。
答えに追加しますが、匿名配列と注釈を使用します:
Hash[*("a,b,c,d".split(',').zip([1,2,3,4]).flatten)]
内側から始めて、その答えを分解します。
"a,b,c,d"
実際には文字列です。split
配列にコンマで。zip
次の配列と一緒にそれ。[1,2,3,4]
実際の配列です。中間結果は次のとおりです。
[[a,1],[b,2],[c,3],[d,4]]
その後、flattenはそれを次のように変換します。
["a",1,"b",2,"c",3,"d",4]
その後:
*["a",1,"b",2,"c",3,"d",4]
それを展開する
"a",1,"b",2,"c",3,"d",4
これをHash[]
メソッドの引数として使用できます。
Hash[*("a,b,c,d".split(',').zip([1,2,3,4]).flatten)]
これにより、
{"a"=>1, "b"=>2, "c"=>3, "d"=>4}
*
)なしでも機能し、flatten:Hash[("a,b,c,d".split(',').zip([1,2,3,4]))]
=> {"a"=>1, "b"=>2, "c"=>3, "d"=>4}
です。私が追加した回答の詳細。
このような配列がある場合-
data = [["foo",1,2,3,4],["bar",1,2],["foobar",1,"*",3,5,:foo]]
そして、各配列の最初の要素をハッシュのキーにし、残りの要素を値の配列にする場合、次のようにします-
data_hash = Hash[data.map { |key| [key.shift, key] }]
#=>{"foo"=>[1, 2, 3, 4], "bar"=>[1, 2], "foobar"=>[1, "*", 3, 5, :foo]}
それが最善の方法かどうかはわかりませんが、これはうまくいきます:
a = ["apple", 1, "banana", 2]
m1 = {}
for x in (a.length / 2).times
m1[a[x*2]] = a[x*2 + 1]
end
b = [["apple", 1], ["banana", 2]]
m2 = {}
for x,y in b
m2[x] = y
end