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

Wiz*_*ott 70 ruby arrays hash

我有一个简单的数组:

arr = ["apples", "bananas", "coconuts", "watermelons"]
Run Code Online (Sandbox Code Playgroud)

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

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

h = {}
arr.each { |a| h[a] = f(a) }
Run Code Online (Sandbox Code Playgroud)

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

h = arr.(???) { |a| a => f(a) }
Run Code Online (Sandbox Code Playgroud)

可以这样做吗?

mic*_*ino 120

假设您有一个功能名称:"f"

def f(fruit)
   fruit + "!"
end

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

会给你:

{"watermelons"=>"watermelons!", "bananas"=>"bananas!", "apples"=>"apples!", "coconuts"=>"coconuts!"}
Run Code Online (Sandbox Code Playgroud)

更新:

正如评论中所提到的,Ruby 1.8.7为此引入了更好的语法:

h = Hash[arr.collect { |v| [v, f(v)] }]
Run Code Online (Sandbox Code Playgroud)

  • 只有一件事 - 为什么`*arr.collect`旁边有一个`*`? (3认同)
  • @Jeriko - splat运算符`*`根据上下文将列表收集到数组中或将数组展开到列表中.在这里,它将数组展开为一个列表(用作新哈希的项目). (3认同)
  • 在Ruby 1.8.7中,丑陋的`Hash [*key_pairs.flatten]`只是`Hash [key_pairs]`.如果你还没有从1.8.6更新,那就更好了,并且"需要'后退'. (3认同)
  • 在查看Jörg的答案并考虑更多之后,请注意您可以删除`*`和`flatten`以获得更简单的版本:`h = Hash [arr.collect {| v | [v,f(v)]}]`.但是,我不确定是否有一个我没看到的问题. (2认同)

dma*_*ylo 52

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

arr 是ActiveRecord对象的集合.

Benchmark.measure {
    100000.times {
        Hash[arr.map{ |a| [a.id, a] }]
    }
}
Run Code Online (Sandbox Code Playgroud)

基准@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] }]
    }
}
Run Code Online (Sandbox Code Playgroud)

基准@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 }
    }
}
Run Code Online (Sandbox Code Playgroud)

基准@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 }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

结论

仅仅因为Ruby具有表现力和动态性,并不意味着你应该总是选择最漂亮的解决方案.基本的每个循环在创建哈希时最快.

  • 你,我的朋友,做你的功课和发布它真棒! (7认同)
  • 考虑到“.collect”只是“.map”的别名,我对“arr.map”和“arr.collect”之间的区别有点困惑 (2认同)

meg*_*gas 32

h = arr.each_with_object({}) { |v,h| h[v] = f(v) }
Run Code Online (Sandbox Code Playgroud)

  • 这比使用Hash [arr.collect {...}]简洁得多. (2认同)

Jör*_*tag 11

这就是我可能会写的:

h = Hash[arr.zip(arr.map(&method(:f)))]
Run Code Online (Sandbox Code Playgroud)

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

  • 我和下一个人一样喜欢“zip”,但是既然我们已经调用了“map”,为什么不把它留在这里呢?`h = 哈希[ arr.map { |v| [ v, f(v) ] } ]` 您的版本是否有我没有看到的优点? (2认同)
  • 我希望它更具可读性. (2认同)

Tim*_*try 10

Ruby 2.6.0通过将一个块传递给to_h方法来实现较短的语法:

arr.to_h { |a| [a, f(a)] }
Run Code Online (Sandbox Code Playgroud)


Vla*_*gel 5

I'm doing it like described in this great article 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)) }
Run Code Online (Sandbox Code Playgroud)

More info about inject method: http://ruby-doc.org/core-2.0.0/Enumerable.html#method-i-inject