在Ruby中,我如何从数组中做一个散列?

我有一个简单的数组:

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

我也有一个函数f ,将执行单个stringinput操作并返回一个值。 这个操作非常昂贵,所以我想在hash里记下结果。

我知道我可以用这样的东西做所需的散列:

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

我想要做的就是不必初始化h,这样我就可以写下这样的内容:

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

可以这样做吗?

假设你有一个很有名的function:“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)] }] 

对一些给定的答案做了一些快速,肮脏的基准。 (这些发现可能不完全相同,基于Ruby版本,怪异的caching等,但一般的结果将是类似的。)

arr是ActiveRecord对象的集合。

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

Benchmark @ 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] }] } } 

Benchmark @ 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 } } } 

Benchmark @ 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 } } } 

Benchmark @ real = 1.650568,@ cstime = 0.0,@ cutime = 0.0,@ stime = 0.12999999999999998,@ utime = 1.51,@ total = 1.64

结论是

仅仅因为Ruby是富有performance力和动感的,并不意味着你应该总是select最漂亮的解决scheme。 基本的每个循环是创build一个散列最快的。

 h = arr.each_with_object({}) { |v,h| h[v] = f(v) } 

这是我可能会写的:

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

简单,清楚,明显,陈述。 你还能想要什么?

我这样做就像这篇文章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

另一个,稍微更清晰的恕我直言 –

 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 >