这是我到目前为止所得到的:
pages = []
pages << { :uri => 'a', :page => 'd' }
pages << { :uri => 'b', :page => 'e' }
pages << { :uri => 'c', :page => 'f' }
pages.each do |page|
puts page.has_value? 'b'
end
但我想要的只是一个真实或错误的答案,例如,你是否包含'e'或'f'?
答案 0 :(得分:6)
pages.any?{|e| e.has_value? 'b'} #=> returns true or false
答案 1 :(得分:2)
您拥有的这种结构一旦变大就会导致查找速度变慢。如果您的数据集很大,并且您没有后端数据库来存储内容以及要搜索的索引字段,那么您可以通过预先构建一个列表来减少内存中搜索。值并搜索它而不是迭代数组并窥视哈希。这样做一次,当哈希数组已经创建并且稳定时。
vals = pages.inject([]){ |m,h| m += h.values; m } #=> ["a", "d", "b", "e", "c", "f"]
如果您必须反复进行查找,那么将其分配给变量将加快查看您是否具有相关值的任务:
vals.include?('a') #=> true
vals.include?('z') #=> false
如果你的哈希值中有很多重复项,那么使用set而不是数组可能是值得的。
require 'set'
pages.inject([].to_set){ |m,h| m += h.values; m } #=> #<Set: {"a", "d", "b", "e", "c", "f"}>
Set的优点是它只保存任何特定元素的一个副本;忽略重复项,使搜索列表尽可能小。缺点是Set在创建时会有更多的开销,从而减慢了创建时间。集合虽然基于Hashes,但它们比顺序搜索更快。
为了显示对数组的迭代速度有多慢,这里是一个基准搜索52个哈希,分别寻找'A'或'z',第一个或最后一个元素。第二个构建值列表然后测试包含。最后两个只使用Sets而不是Arrays。
require 'benchmark'
require 'set'
puts "RUBY_VERSION=#{ RUBY_VERSION }"
# build a 52-element array.
pages = ( ('A'..'Z').to_a + ('a'..'z').to_a ).each_slice(2).inject([]) { |m,a| m << { uri:a[0], page:a[1] }; m }
n = 500_000
Benchmark.bm(20) do |x|
x.report("AoH traversal A") { n.times { pages.any?{ |h| h.has_value?('A') } } }
x.report("AoH traversal z") { n.times { pages.any?{ |h| h.has_value?('z') } } }
x.report("array inclusion A") { vals = pages.inject([]){ |m,h| m += h.values; m }; n.times { vals.include?('A') } }
x.report("array inclusion z") { vals = pages.inject([]){ |m,h| m += h.values; m }; n.times { vals.include?('z') } }
x.report("set inclusion A") { vals = pages.inject([].to_set){ |m,h| m += h.values; m }; n.times { vals.include?('A') } }
x.report("set inclusion z") { vals = pages.inject([].to_set){ |m,h| m += h.values; m }; n.times { vals.include?('z') } }
end
# >> RUBY_VERSION=1.9.2
# >> user system total real
# >> AoH traversal A 1.140000 0.000000 1.140000 ( 1.140952)
# >> AoH traversal z 19.130000 0.010000 19.140000 ( 19.135050)
# >> array inclusion A 0.450000 0.000000 0.450000 ( 0.443042)
# >> array inclusion z 5.600000 0.010000 5.610000 ( 5.605876)
# >> set inclusion A 0.490000 0.000000 0.490000 ( 0.492484)
# >> set inclusion z 0.480000 0.000000 0.480000 ( 0.479374)
编辑:
每次插入插件时,我都需要先进行检查。这是网络蜘蛛的一部分。我将来会考虑一个数据库。
查看基准测试的结果。
您选择的答案和假设实施的解决方案平均比使用Set进行查找慢20倍。你可以保持一个Set,你的结构并且仍然领先,因为Set的查找速度会慢得多。即使单独维护阵列也要快10倍左右。
例如,选中Set for a hit或miss。如果它是一个命中移动到下一页。如果您的哈希数组中的信息遗漏push
,则将必要的命中信息添加到下一循环的Set中。不要每次都完全重建Set,只附加新信息。
在一个快速而肮脏的蜘蛛中,我会使用哈希,其中的键是我扫描的URL。我通过从中删除所有查询,会话和其他数据来规范化它们,只留下页面的基本URL。这样我就可以跟踪我是否已经看过该页面并跳过它,否则当查询发生变化时,您最终会多次点击同一页面。哈希键指向包含我从扫描页面中收集到的所有信息的结构,因此,在处理结束时,我可以通过遍历哈希的键来转储每个页面的结果。
使用数据库时也适用相同的策略,否则您可以使用冗余页面扫描填充表格,而不是实际查看唯一页面。