簡體   English   中英

在Ruby中,如何從數組中生成哈希?

[英]In Ruby, how do I make a hash from an array?

我有一個簡單的數組:

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

我還有一個函數f ,它將對單個字符串輸入執行操作並返回一個值。 該操作非常昂貴,因此我想在哈希中記住結果。

我知道我可以用以下方式進行所需的哈希:

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

我想做的是不必初始化h,這樣我就可以編寫如下代碼:

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

能做到嗎?

假設您有一個函數,函數名稱為:“ 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版本,奇怪的緩存等的發現並不完全相同,但是總體結果將是相似的。)

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具有表現力和動態性,並不意味着您應該始終尋求最漂亮的解決方案。 基本的每個循環在創建哈希中最快。

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

我可能會這樣寫:

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

簡單,清晰,明顯,說明性。 你還能想要什么?

Ruby 2.6.0通過將一個塊傳遞給to_h方法來實現較短的語法:

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

我正在按照這篇出色的文章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 >

除了弗拉多·辛格(Vlado Cingel)的答案(我還不能添加評論,所以我添加了答案)。

注入也可以通過這種方式使用:該塊必須返回累加器。 僅塊中的分配返回分配的值,並報告錯誤。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM