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

Дано:

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

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

a2 = [2, 6, 15]

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

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

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

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

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

Ответ 1

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

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

b - a
=> [15]

(b - a).empty?
=> false

Ответ 2

Возможно, это проще читать:

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

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

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

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

(a1 & a2) == a1

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

Ответ 3

Это можно сделать, выполняя

(a2 & a1) == a2

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

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

Ответ 4

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

Ответ 5

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

Ответ 6

Вы можете обезвредить класс 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