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

141

Дано:

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

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

a2 = [2, 6, 15]

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

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

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

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

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

Теги:
arrays

6 ответов

230
Лучший ответ
a = [5, 1, 6, 14, 2, 8]
b = [2, 6, 15]

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

b - a
=> [15]

(b - a).empty?
=> false
  • 58
    Это путь. Это может быть просто сокращено до (a2-a1).empty?
  • 7
    Это работает только для массивов, которые являются наборами, но не для массивов с дубликатами
Показать ещё 2 комментария
60

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

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

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

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

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

(a1 & a2) == a1

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

  • 0
    Если вы используете определение OP a1 и a2 и a1, "содержащих все элементы" a2, я думаю, что это должно быть _ (a1 & a2) .size == a2.size _, поскольку a2 - это меньший массив, который должен иметь все элементы, включенные в больший массив (чтобы получить «истину») - следовательно, пересечение двух массивов должно быть той же длины, что и меньший массив, если все элементы в нем присутствуют в большем.
48

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

(a2 & a1) == a2

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

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

  • 2
    использование метода length будет работать намного лучше
  • 3
    Это не будет работать, если набор пересечений имеет одинаковые элементы в другом порядке. Я нашел это трудным путем, пытаясь ответить на этот вопрос: stackoverflow.com/questions/12062970/… позже понял, что многие умные люди уже сделали это здесь!
Показать ещё 2 комментария
9

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

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

  • 0
    Вы можете сделать это в O (n) с помощью счетной сортировки.
  • 0
    Нет, ты не можешь. Счетная сортировка использует ограниченную вселенную, и у Ruby нет ограничений на то, как большие числа могут быть получены.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню