我有一个包含大约50,000个值(哈希)的大型数组(A)。我有2个其他数组包含黑名单(B)和值(也是字符串)的白名单(W)要检查A.我知道Array.include?函数,但我必须检查B或W中的值是否部分位于A中每个哈希值的关键“文本”中。
例如:
A = [ { :text => 'Cat', :some_other_key => 'bla' },
{ :text => 'Dog', :some_other_key => 'blub' },
{ :text => 'Wolve', :some_other_key => 'example' },
{ :text => 'Bird', :some_other_key => 'test' },
{ :text => 'White Whale', :some_other_key => 'test' } ]
W = [ 'Whale', 'Cow' ]
B = [ 'Pig', 'Chicken' ]
我希望将'白鲸'与来自W的'鲸鱼'相匹配。据我所知,包括?只有在值完全相同时才会匹配。
到目前为止我所做的是:
A.each do |value|
if W.any? { |w| value[:text] =~ /#{w}/ }
#Do stuff
next
elsif B.any? { |w| value[:text] =~ /#{w}/ }
#Do other stuff
next
end
end
这适合我,但如果A,B或W变大,它真的很慢。这最终引出了我的问题:有没有人知道更好更快的方法来实现这个目标?
答案 0 :(得分:2)
构建正则表达式是一种可以移出内循环的成本:
w_rx = W.map { |w| /#{w}/ }
b_rx = B.map { |w| /#{w}/ }
A.each do |value|
if w_rx.any? { |rx| value[:text] =~ rx }
#Do stuff
next
elsif b_rx.any? { |rx| value[:text] =~ rx }
#Do other stuff
next
end
end
另请查看steenslag的回答,将w_rx = W.map { |w| /#{w}/ }
替换为big_w_rx = Regepx.union(W)
,将w_rx.any? { |rx| value[:text] =~ rx }
替换为value[:text] =~ big_w_rx
可以更快。我测试它的速度提高了5倍,但这将取决于你的数据。
改进循环的唯一方法是找到一种方法来跳过A,W或B中的条目。这实际上是索引的作用,但是你在这里遇到的问题是A中的每个条目都是新的你,所以任何方案索引的成本可能都高于扫描整个列表的成本。
如果您对每个[:text]
的结构有一定的了解,那么您可以将它用于您的优势。例如,如果[:text]
的长度通常小于W
和B
中的项,则无需检查与较长字符串的匹配。按长度排序W
和B
,创建每个长度的点在排序列表中的位置的哈希值,并且仅测试内部循环中W和B的最小切片 - '显然非常具体到数据的行为方式,因此我不打算将其全部写出来,它可能对您没有帮助。
基准,Regexp.union
与arr_of_regex.any?
require 'benchmark'
W = [ 'Whale', 'Cow', 'Flat', 'Bundle', 'Bubbles',
'Clarinet', 'Cash', 'Fish', 'Donkey' ]
# Dummy data (lots of misses to W, so might not be representative)
a = Array.new( 100000 ) { |i|
(1..(5+rand(20))).map { |j|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".chars.sample
}.join
}
big_rx = Regexp.union(W)
w_rx = W.map { |w| /#{w}/ }
Benchmark.bm do |bm|
bm.report(:big_rx) { a.select { |s| s =~ big_rx } }
bm.report(:w_rx) { a.select { |s| w_rx.any? { |rx| s =~ rx } } }
end
输出:
user system total real
big_rx 0.110000 0.000000 0.110000 ( 0.112621)
w_rx 0.650000 0.000000 0.650000 ( 0.660112)
如果我将W修改为1000条记录,与上面示例中的a
类似,那么差异就会更接近2(仍然非常值得拥有):
user system total real
big_rx 21.380000 0.010000 21.390000 ( 21.398051)
w_rx 39.720000 0.030000 39.750000 ( 39.759734)
答案 1 :(得分:2)