数组是否包含来自另一个数组的任何值?


155

测试数组是否包含第二个数组中的任何元素的最有效方法是什么?

下面的两个示例试图回答问题,但确实foods包含来自的任何元素cheeses

cheeses = %w(chedder stilton brie mozzarella feta haloumi reblochon)
foods = %w(pizza feta foods bread biscuits yoghurt bacon)

puts cheeses.collect{|c| foods.include?(c)}.include?(true)

puts (cheeses - foods).size < cheeses.size

Answers:


268
(cheeses & foods).empty?

正如Marc-AndréLafortune在评论中说的那样,&线性工作时间为any?+ ,而+ 则为include?平方。对于更大的数据集,线性时间会更快。对于小型数据集,any?+ include?可能更快,如Lee Jarvis的回答所示-可能是因为&分配了一个新的Array,而另一种解决方案却没有,并且分配了一个简单的嵌套循环来返回布尔值。


3
当检查一个数组是否包含另一个数组中的元素时,这样做(奶酪和食品)更有意义吗?因为如果数组实际上包含任何相同的元素,这将返回一个真值?
Ryan Francis

1
@RyanFrancis,文档:any?如果所述块曾经返回比假或零以外的值的方法返回true。 empty?如果self不包含任何元素,则返回true。
Nakilon 2014年

3
@Nakilon我也很困惑,为什么不是答案不是(cheeses & foods).any?OP的问题:奶酪中是否有食物?在他的示例中,“羊乳酪”同时存在,所以结果应该为真,对吗?那么为什么要检查.empty?路口呢?
SuckerForMayhem

@SuckerForMayhem,因为OP的问题是“是否有……?”,而不仅仅是“如果有?”。如果省略“ are ... ”,则假定它为“ If any is True? ”,并且对于像这样的数组将返回False [false, false, false],而它显然不是空的。
Nakilon

在activerecord级别上有什么实现吗?
李俊和

35

如何可枚举#否?

>> cheeses = %w(chedder stilton brie mozzarella feta haloumi)
=> ["chedder", "stilton", "brie", "mozzarella", "feta", "haloumi"]
>> foods = %w(pizza feta foods bread biscuits yoghurt bacon)
=> ["pizza", "feta", "foods", "bread", "biscuits", "yoghurt", "bacon"]
>> foods.any? {|food| cheeses.include?(food) }
=> true

基准脚本:

require "benchmark"
N = 1_000_000
puts "ruby version: #{RUBY_VERSION}"

CHEESES = %w(chedder stilton brie mozzarella feta haloumi).freeze
FOODS = %w(pizza feta foods bread biscuits yoghurt bacon).freeze

Benchmark.bm(15) do |b|
  b.report("&, empty?") { N.times { (FOODS & CHEESES).empty? } }
  b.report("any?, include?") { N.times { FOODS.any? {|food| CHEESES.include?(food) } } }
end

结果:

ruby version: 2.1.9
                      user     system      total        real
&, empty?         1.170000   0.000000   1.170000 (  1.172507)
any?, include?    0.660000   0.000000   0.660000 (  0.666015)

您可以通过将cheeses其设置为一组来改进此设置。
阿库恩

1
我在红宝石2.2.7和2.3.4上运行了自己的基准测试,它any?, include?是最快的,但设置得最慢却与之
Jared

4
该基准受所提到的特定示例所偏见,并不一定适用于更一般的情况。如果两个数组之间没有公共元素怎么办?如果每次通过时数组的顺序不同怎么办?如果羊乳酪出现在两个阵列的末端怎么办?正如Marc-André所述,集合交集是在线性时间内执行的,因此有意义的是,它在一般情况下具有更大的可伸缩性,而不是纯粹用来阐明问题的一个特定示例。
user2259664

22

您可以检查交叉点是否为空。

cheeses = %w(chedder stilton brie mozzarella feta haloumi)
foods = %w(pizza feta foods bread biscuits yoghurt bacon)
foods & cheeses
=> ["feta"] 
(foods & cheeses).empty?
=> false

1
Set.new(cheeses).disjoint? Set.new(foods)

同样在我的(非科学的)基准测试中,不相交的集比其他方法慢得多:gist.github.com/jaredmoody/d2a1e83de2f91fd6865920cd01a8b497
Jared

1
感谢您的意见。我不确定为什么它不是Set.new,但我只是对其进行了编辑。我在2.4.1中尝试了您的性能基准。我的表现更好,但使用包含更多单词的脱节集仍然不是最好的。我在您的要旨上发表了我的看法。我也认为disjoint?是非常优雅的,特别是相比于“任何?,包括?”。最初的问题确实询问了优雅和高效。
davidkovsky

.to_set方法在这里可能有用cheeses.to_set.disjoint?(foods.to_set)
itsnikolay

0
require "benchmark"
N = 1_000_000
puts "ruby version: #{RUBY_VERSION}"

CHEESES = %w(chedder stilton brie mozzarella feta haloumi).freeze
FOODS = %w(pizza feta foods bread biscuits yoghurt bacon).freeze

Benchmark.bm(15) do |b|
  b.report("&, empty?") { N.times { (FOODS & CHEESES).empty? } }  
  b.report("any?, include?") { N.times { FOODS.any? {|food| CHEESES.include?(food) } } }  
  b.report("disjoint?") { N.times { FOODS.to_set.disjoint? CHEESES.to_set }}
end  
                      user     system      total        real
&, empty?         0.751068   0.000571   0.751639 (  0.752745)
any?, include?    0.408251   0.000133   0.408384 (  0.408438)
disjoint?        11.616006   0.014806  11.630812 ( 11.637300)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.