Как определить, содержит ли один массив все элементы другого массива

дано:

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

Я хотел бы определить, содержит ли он все элементы:

a2 = [2, 6, 15]

в этом случае результат false.

существуют ли какие-либо встроенные методы Ruby/Rails для идентификации такого включения массива?

один из способов реализовать это:

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

есть ли лучший, более читаемый способ?

6 ответов


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

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

b - a
=> [15]

(b - a).empty?
=> false

возможно, это легче читать:

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

вы также можете использовать пересечение массива:

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

отметим, что size здесь используется только для скорости, вы также можете сделать (медленнее):

(a1 & a2) == a1

но я думаю, что первый более читаемый. Эти 3-простой Рубин (не рельсы).


это может быть достигнуто путем делать

(a2 & a1) == a2

это создает пересечение обоих массивов, возвращая все элементы a2, который также a1. Если результат такой же, как a2, вы можете быть уверены, что все элементы включены в a1.

этот подход работает только если все элементы a2 отличаются друг от друга в первую очередь. Если есть двойники, этот подход терпит неудачу. Один из Tempos все еще работает, поэтому я искренне порекомендуйте его подход (также это, вероятно, быстрее).


Если нет повторяющихся элементов или вы не заботитесь о них, то вы можете использовать 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) }

в зависимости от того, насколько велики ваши массивы, вы можете рассмотреть эффективный алгоритм 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). Другие алгоритмы не могут быть быстрее (асимптотически) с использованием несортированных массивов.


вы можете обезьяна-патч класса 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