在Ruby中,如何从数组中创建哈希?

时间:2010-05-31 12:12:05

标签: ruby arrays hash

我有一个简单的数组:

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

我还有一个函数f,它将对单个字符串输入执行操作并返回一个值。这个操作非常昂贵,所以我想在哈希中记住结果。

我知道我可以用这样的东西制作所需的哈希:

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

我想要做的是不必初始化h,以便我可以写下这样的东西:

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

能做到吗?

8 个答案:

答案 0 :(得分:116)

假设您有一个具有虚假名称的功能:“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)] }]

答案 1 :(得分:50)

对某些给定的答案做了一些快速,肮脏的基准测试。 (这些发现可能与您基于Ruby版本,奇怪的缓存等完全相同,但一般结果将类似。)

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具有表现力和动态性,并不意味着你应该总是选择最漂亮的解决方案。基本的每个循环在创建哈希方面是最快的。

答案 2 :(得分:30)

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

答案 3 :(得分:11)

这是我可能会写的:

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

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

答案 4 :(得分:6)

Ruby 2.6.0通过passing a block to the to_h method启用了较短的语法:

<!DOCTYPE html>
<html>
	<head>
		<title>Page Title</title>
	</head>
	<body>
		<button onclick="alert(UserInput('prompt with input', ''))">prompt with input</button><br/>
		<button onclick="alert(UserInput('prompt with mandantory input', '', 0))">prompt with mandantory input</button>
	</body>
</html>

答案 5 :(得分:5)

我正如本文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

答案 6 :(得分:1)

另一个,稍微更清楚恕我直言 -

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 >

答案 7 :(得分:0)

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

Inject也可以这种方式使用:该块必须返回累加器。只有块中的赋值会返回赋值,并报告错误。

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