配列に別の配列の値が含まれていますか?


155

配列に2番目の配列の要素が含まれているかどうかをテストする最も効率的な方法は何ですか?

以下の2つの例では、質問に回答しようとすると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

回答:


268
(cheeses & foods).empty?

Marc-AndréLafortuneがコメントで言ったように、&線形時間で動作しますが、any?+ include?は2 次になります。データのセットが大きいほど、線形時間は速くなります。小さなデータセットの場合、any?+ include?はLee Jarvisの回答が示すように高速になる可能性があります。おそらく、&別のソリューションが新しい配列を割り当てず、ブール値を返す単純なネストループとして機能するためです。


3
配列に別の配列の要素が含まれているかどうかを確認する場合、(チーズと食べ物)を行うほうが理にかなっていると思いませんか?これは、配列に実際に同じ要素が含まれている場合にtrue値を返すためですか?
ライアンフランシス14

1
@RyanFrancis、ドキュメント:any?ブロックが今まで偽かnil以外の値を返す場合、このメソッドはtrueを返します。 empty?自分自身に要素が含まれていない場合はtrueを返します。
ナキロン2014

3
@Nakilon私も混乱しています。なぜ答えが(cheeses & foods).any?OPの質問ではないのですか。食べ物がチーズに入っているかどうかです。彼の例では、「フェタ」が両方にあるので、結果は正しいはずですよね?では、なぜ.empty?交差点を確認するのでしょうか。
SuckerForMayhem 2016年

@ SuckerForMayhem、OPの質問は「もしあれば...」ではなく、「もしあれば」ですか。" are ... "が省略された場合、 "If any is True? "と見なされ、のような配列に対してFalseを返します[false, false, false]、明らかに空ではありません。
Nakilon、2016年

activerecordレベルでの実装はありますか?
Lee Chun Hoe

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セットにすることでこれを改善できます。
akuhn

1
ルビー2.2.7と2.3.4にここに私自身のベンチマークを実行し、any?, include?:最も遅い最速、セットばらばらだったgist.github.com/jaredmoody/d2a1e83de2f91fd6865920cd01a8b497
ジャレド

4
このベンチマークは、言及された特定の例によってバイアスされており、必ずしもより一般的なケースには当てはまりません。2つの配列の間に共通の要素がない場合はどうなりますか?配列が各パスで異なる順序であった場合はどうなりますか?両方のアレイの最後にフェタが現れた場合はどうなりますか?Marc-Andréが述べたように、set交差は線形時間で実行されるため、質問を明確にするために純粋に使用される特定の例ではなく、一般的なケースに対してはるかにスケーラブルであることが理にかなっています。
user2259664 '19年

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)

また、私の(非科学的な)ベンチマークでは、set disjointが他の方法よりも大幅に遅くなりました。gist.github.com
Jared

1
コメントしてくれてありがとう。なぜSet.newでなかったのかはわかりませんが、編集しただけです。2.4.1でパフォーマンスベンチマークを試しました。私のほうが上手でしたが、より多くの単語を含むばらばらのセットを使うのはまだ最善ではありませんでした。私はあなたの趣旨のコメントに私のバージョンを入れました。disjoint?特に「any ?, include?」と比べると、とてもエレガントだと思います。元の質問は、エレガントで効率的であるかについて尋ねました。
davidkovsky

.to_setメソッドはここで役立ちますcheeses.to_set.disjoint?(foods.to_set)
itsnikolay 2018年

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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.