web-dev-qa-db-ja.com

Rubyでは、配列からハッシュを作成するにはどうすればよいですか?

私は単純な配列を持っています:

arr = ["apples", "bananas", "coconuts", "watermelons"]

また、単一の文字列入力に対して操作を実行し、値を返す関数fがあります。この操作は非常に高価なので、結果をハッシュにメモしたいと思います。

私はこのようなもので希望のハッシュを作成できることを知っています:

h = {}
arr.each { |a| h[a] = f(a) }

私がやりたいのは、hを初期化する必要がないため、次のようなものを書くだけです:

h = arr.(???) { |a| a => f(a) }

それはできますか?

69
Wizzlewott

ファンタスティックな名前の関数があるとしましょう: "f"

def f(fruit)
   fruit + "!"
end

arr = ["apples", "bananas", "coconuts", "watermelons"]
h = Hash[ *arr.collect { |v| [ v, f(v) ] }.flatten ]

あなたに与えます:

{"watermelons"=>"watermelons!", "bananas"=>"bananas!", "apples"=>"apples!", "coconuts"=>"coconuts!"}

更新しました:

コメントで述べたように、Ruby 1.8.7はこのためのより良い構文を導入します:

h = Hash[arr.collect { |v| [v, f(v)] }]
118
microspino

与えられた回答のいくつかについて、いくつかの迅速で汚いベンチマークを行いました。 (これらの結果は、Rubyバージョン、奇妙なキャッシュなどに基づいて、あなたのものと正確に同一ではないかもしれませんが、一般的な結果は似ています。)

arrはActiveRecordオブジェクトのコレクションです。

Benchmark.measure {
    100000.times {
        Hash[arr.map{ |a| [a.id, a] }]
    }
}

ベンチマーク@ real = 0.860651、@ cstime = 0.0、@ cutime = 0.0、@ stime = 0.0、@ utime = 0.8500000000000005、@ total = 0.8500000000000005

Benchmark.measure { 
    100000.times {
        h = Hash[arr.collect { |v| [v.id, v] }]
    }
}

ベンチマーク@ real = 0.74612、@ cstime = 0.0、@ cutime = 0.0、@ stime = 0.010000000000000009、@ utime = 0.740000000000002、@ total = 0.750000000000002

Benchmark.measure {
    100000.times {
        hash = {}
        arr.each { |a| hash[a.id] = a }
    }
}

ベンチマーク@ real = 0.627355、@ cstime = 0.0、@ cutime = 0.0、@ stime = 0.010000000000000009、@ utime = 0.6199999999999974、@ total = 0.6299999999999975

Benchmark.measure {
    100000.times {
        arr.each_with_object({}) { |v, h| h[v.id] = v }
    }
}

ベンチマーク@ real = 1.650568、@ cstime = 0.0、@ cutime = 0.0、@ stime = 0.12999999999999998、@ utime = 1.51、@ total = 1.64

結論として

Rubyが表現力豊かで動的であるからといって、常に最もきれいな解決策に進む必要があるというわけではありません。基本的な各ループがハッシュを作成するのに最速でした。

52
dmastylo
h = arr.each_with_object({}) { |v,h| h[v] = f(v) }
32
megas

これはおそらく私が書くものです:

h = Hash[arr.Zip(arr.map(&method(:f)))]

シンプル、明確、明白、宣言的。これ以上何が欲しいですか?

11
Jörg W Mittag

Ruby 2.6.0では、 ブロックをto_hメソッド

arr.to_h { |a| [a, f(a)] }
8
Timitry

私はこの素晴らしい記事で説明されているようにそれをやっています http://robots.thoughtbot.com/iteration-as-an-anti-pattern#build-a-hash-from-an-array

array = ["apples", "bananas", "coconuts", "watermelons"]
hash = array.inject({}) { |h,fruit| h.merge(fruit => f(fruit)) }

injectメソッドの詳細: http://Ruby-doc.org/core-2.0.0/Enumerable.html#method-i-inject

5
Vlado Cingel

もう一つ、やや明確な私見-

Hash[*array.reduce([]) { |memo, fruit| memo << fruit << f(fruit) }]

f()-として長さを使用する-

2.1.5 :026 > array = ["apples", "bananas", "coconuts", "watermelons"]
 => ["apples", "bananas", "coconuts", "watermelons"] 
2.1.5 :027 > Hash[*array.reduce([]) { |memo, fruit| memo << fruit << fruit.length }]
 => {"apples"=>6, "bananas"=>7, "coconuts"=>8, "watermelons"=>11} 
2.1.5 :028 >
1
Shreyas