1つの配列に別の配列のすべての要素が含まれているかどうかを確認する方法


179

与えられた:

a1 = [5, 1, 6, 14, 2, 8]

次のすべての要素が含まれているかどうかを確認したいと思います。

a2 = [2, 6, 15]

この場合の結果はfalseです。

そのような配列の包含を識別するための組み込みのRuby / Railsメソッドはありますか?

これを実装する1つの方法は次のとおりです。

a2.index{ |x| !a1.include?(x) }.nil?

より良い、より読みやすい方法はありますか?


受け入れられた答え(配列減算)が最速のソリューションです。私はここでそれらすべてをベンチマークしました:gist.github.com/bbugh/cbbde8b48cbb16286044f6893e1f2e5f
ブレインバッグ

回答:


309
a = [5, 1, 6, 14, 2, 8]
b = [2, 6, 15]

a - b
=> [5, 1, 14, 8]

b - a
=> [15]

(b - a).empty?
=> false

61
これは進むべき道です。少し短くなるかもしれません(a2-a1).empty?
Holger Just

9
これは、セットの配列でのみ機能し、重複のある配列では機能しません
Chris

3
@Chris-そのためにArray#uniqを使用してみてください。Holger Just氏の例では、それは(a2.uniq - a1.uniq).empty?
Nick

stackoverflow.com/questions/135538​​22/…は、私が意図したものです。Array#uniqueは明示的に失敗します。
Chris

81

おそらくこれは読みやすいでしょう:

a2.all? { |e| a1.include?(e) }

配列の交差を使用することもできます。

(a1 & a2).size == a1.size

ここでsizeは速度のためだけに使用されていることに注意してください。次のこともできます(遅い):

(a1 & a2) == a1

しかし、私は最初の方が読みやすいと思います。これら3つはプレーンルビーです(レールではありません)。


OPのa1とa2の定義、およびa1がa2の「すべての要素を含む」を使用している場合、これは_(a1&a2).size == a2.size _になるはずです。大きい配列に含まれる要素(「true」を取得するため)-したがって、2つの配列のすべての要素が大きい配列に存在する場合、2つの配列の共通部分は小さい配列と同じ長さでなければなりません。
JosephK

57

これは、

(a2 & a1) == a2

これにより、両方の配列の共通部分が作成され、a2そこにもあるすべての要素が返されa1ます。結果がと同じ場合は、a2すべての要素がに含まれていることを確認できますa1

このアプローチが機能するa2のは、そもそもすべての要素が互いに異なる場合のみです。ダブルがある場合、このアプローチは失敗します。テンポスのものはそれでもまだ機能するので、私は心から彼のアプローチをお勧めします(おそらくもっと速いです)。


2
このlength方法を使用するとパフォーマンスが大幅に向上します
パブロフェルナンデス

3
これは、交差セットに同じ要素が異なる順序で含まれている場合は機能しません。この質問に答えるためにしようとしているときに、私はハードな方法からこれを見つけた:stackoverflow.com/questions/12062970/...後に実現し、多くのスマートな人々はすでにそれをここで行っていました!
CubaLibre 2012

1
@CubaLibre興味深い。これを再現するためのテストデータはありますか?私のテストでは、結果の配列が最初の配列の要素の順序を保持しているように見えました(そのため、私の回答に対する最新の編集)。しかし、もしそうでなければ、私は学びたいです。
Holgerが2012

@HolgerJust(a2&a1)の代わりに(a1&a2)を実行するというミスを犯したため、エラーが表示されました。あなたは正しいと最初の配列からの順序を保持しています。
CubaLibre 2012

10

重複する要素がないか、それらを気にしない場合は、Setクラスを使用できます。

a1 = Set.new [5, 1, 6, 14, 2, 8]
a2 = Set.new [2, 6, 15]
a1.subset?(a2)
=> false

これが使用する舞台裏

all? { |o| set.include?(o) }

1

Arrayクラスをサルパッチすることができます:

class Array
    def contains_all?(ary)
        ary.uniq.all? { |x| count(x) >= ary.count(x) }
    end
end

テスト

irb(main):131:0> %w[a b c c].contains_all? %w[a b c]
=> true
irb(main):132:0> %w[a b c c].contains_all? %w[a b c c]
=> true
irb(main):133:0> %w[a b c c].contains_all? %w[a b c c c]
=> false
irb(main):134:0> %w[a b c c].contains_all? %w[a]
=> true
irb(main):135:0> %w[a b c c].contains_all? %w[x]
=> false
irb(main):136:0> %w[a b c c].contains_all? %w[]
=> true
irb(main):137:0> %w[a b c d].contains_all? %w[d c h]
=> false
irb(main):138:0> %w[a b c d].contains_all? %w[d b c]
=> true

もちろん、メソッドは標準の単独メソッドとして書くことができます、例えば

def contains_all?(a,b)
    b.uniq.all? { |x| a.count(x) >= b.count(x) }
end

そしてあなたはそれを次のように呼び出すことができます

contains_all?(%w[a b c c], %w[c c c])

実際、プロファイリング後の次のバージョンははるかに高速で、コードは短くなっています。

def contains_all?(a,b)
    b.all? { |x| a.count(x) >= b.count(x) }
end

0

配列の大きさに応じて、効率的なアルゴリズムO(n log n)を検討できます

def equal_a(a1, a2)
  a1sorted = a1.sort
  a2sorted = a2.sort
  return false if a1.length != a2.length
  0.upto(a1.length - 1) do 
    |i| return false if a1sorted[i] != a2sorted[i]
  end
end

ソートにはO(n log n)がかかり、各ペアのチェックにはO(n)がかかります。したがって、このアルゴリズムはO(n log n)です。他のアルゴリズムは、ソートされていない配列を使用すると(漸近的に)より速くなることはできません。


O(n)でカウントソートを使用して実行できます。
klochner、2011

いいえ、あなたがすることはできません。ソートのカウントでは制限されたユニバースが使用され、Rubyには大きな数値を取得する方法に関する制限はありません。
ayckoster

できます。実際にアイテムを並べ替える必要はないためです。ハッシュマッピングアイテムが必要です->両方の配列のカウントを取得し、キーを反復処理してカウントを比較します。
klochner、2011

Array#sortがマージソートを使用していることを確認しますか?
ネイトSymer 2014

0

(a1-a2)または(a1&a2)に基づくほとんどの回答は、いずれかの配列に重複する要素がある場合は機能しません。単語のすべての文字(配列に分割)が一連の文字(たとえばスクラブル)の一部であるかどうかを確認する方法を探して、ここに到着しました。これらの答えはどれもうまくいきませんでしたが、これはうまくいきました:

def contains_all?(a1, a2)
  try = a1.chars.all? do |letter|
    a1.count(letter) <= a2.count(letter)
  end
  return try
end
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.