如何查找散列数组中包含的项?

时间:2011-03-19 04:51:05

标签: ruby

这是我到目前为止所得到的:

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'?

2 个答案:

答案 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。这样我就可以跟踪我是否已经看过该页面并跳过它,否则当查询发生变化时,您最终会多次点击同一页面。哈希键指向包含我从扫描页面中收集到的所有信息的结构,因此,在处理结束时,我可以通过遍历哈希的键来转储每个页面的结果。

使用数据库时也适用相同的策略,否则您可以使用冗余页面扫描填充表格,而不是实际查看唯一页面。