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