Как определить, содержит ли один массив все элементы другого массива
дано:
a1 = [5, 1, 6, 14, 2, 8]
Я хотел бы определить, содержит ли он все элементы:
a2 = [2, 6, 15]
в этом случае результат false
.
существуют ли какие-либо встроенные методы Ruby/Rails для идентификации такого включения массива?
один из способов реализовать это:
a2.index{ |x| !a1.include?(x) }.nil?
есть ли лучший, более читаемый способ?
6 ответов
возможно, это легче читать:
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