ruby:如何有效地迭代哈希中的元素

时间:2012-08-15 22:46:16

标签: ruby-on-rails ruby arrays hash

我有一个非常大的哈希,我想迭代它。 Hash.each似乎太慢了。 有没有有效的方法来做到这一点?

如何将此哈希转换为数组?


在每个循环中,我都在做非常简单的字符串:

name_hash.each {|name, str|

  record += name.to_s + "\|" + str +"\n"

}

并且哈希使用人名作为键,一些相关内容作为值:

name_hash = {:"jose garcia" => "ca:tw#2@1,2@:th#1@3@;ar:tw#1@4@:fi#1@5@;ny:tw#1@6@;"}

6 个答案:

答案 0 :(得分:3)

考虑以下示例,该示例使用100万个元素的哈希:

#! /usr/bin/env ruby
require 'benchmark'

h = {}
1_000_000.times do |n|
  h[n] = rand
end

puts Benchmark.measure { h.each { |k, v| } }

a = nil
puts Benchmark.measure { a = h.to_a }
puts Benchmark.measure { a.each { |k, v| } }

我在我的系统上运行它(运行Ruby 1.8.5),我得到:

  0.350000   0.020000   0.370000 (  0.380571)
  0.300000   0.020000   0.320000 (  0.307207)
  0.160000   0.040000   0.200000 (  0.198388)

因此迭代数组确实更快(0.16秒对比哈希值为0.35秒)。但是生成阵列需要0.3秒。因此净过程较慢0.46秒而0.35秒。

所以看起来最好只是迭代哈希,至少在这个测试用例中。

答案 1 :(得分:2)

在ruby中这样做的更惯用的方式:

record = name_hash.map{|k,v| "#{k}|#{v}"}.join("\n")

我不知道它与速度的比较,但部分问题可能是因为你不断在字符串上添加一点并在每次迭代时创建新的(越来越长的)字符串对象。连接在C中完成,可能表现更好。

答案 2 :(得分:2)

String#+很慢。这应该改善它

 record = name_hash.map{|line| line.join("|")}.join("\n")

如果你使用它输出到某个地方,你不应该创建一个巨大的字符串,而是逐行写入输出。

答案 3 :(得分:1)

迭代大型集合很慢,每种方法都不会限制它。你在循环中做什么这么慢?如果您需要转换为数组,可以通过调用some_hash.to_a

来实现

答案 4 :(得分:1)

可能“通过进行单个数据库查询”

将大型哈希转换为数组需要创建一个大型对象,并且需要两次迭代,尽管其中一次是解释器的内部并且可能非常快。

这不仅比迭代Hash更快,而且可能适用于大型对象。

查看the Standard Library Benchmark package以获得衡量运行时的简便方法。

我还冒昧地猜测,这里真正的问题是你有一个类似Hash的ActiveRecord对象,它会在枚举的每个循环中对你的数据库服务器进行往返。您真正想要的是绕过AR并运行本机查询以在往返中一次性检索所有内容。

答案 5 :(得分:1)

我原以为ruby 1.9.x已经使哈希迭代变得更快但可能是错误的。如果它是简单的结构,你可以尝试一个不同的哈希,比如https://github.com/rdp/google_hash,这是我为了让#each更可靠而被攻击的一个...