Как определить, содержит ли один массив все элементы другого массива - PullRequest
163 голосов
/ 12 сентября 2011

Учитывая:

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

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

a2 = [2, 6, 15]

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

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

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

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

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

Ответы [ 7 ]

287 голосов
/ 12 сентября 2011
a = [5, 1, 6, 14, 2, 8]
b = [2, 6, 15]

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

b - a
=> [15]

(b - a).empty?
=> false
71 голосов
/ 12 сентября 2011

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

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

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

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

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

(a1 & a2) == a1

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

52 голосов
/ 12 сентября 2011

Этого можно достичь, выполнив

(a2 & a1) == a2

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

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

10 голосов
/ 12 сентября 2011

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

Вы можете сделать обезьянь-патч для класса Array:

class Array
    def contains_all?(ary)
        ary.uniq.all? { |x| count(x) >= ary.count(x) }
    end
end

test

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 голосов
/ 15 января 2019

Большинство ответов, основанных на (a1 - a2) или (a1 и a2), не сработают, если в любом из этих массивов есть повторяющиеся элементы.Я прибыл сюда в поисках способа проверить, все ли буквы слова (разбитые на массивы) являются частью набора букв (например, для скрэббл).Ни один из этих ответов не сработал, но вот этот:

def contains_all?(a1, a2)
  try = a1.chars.all? do |letter|
    a1.count(letter) <= a2.count(letter)
  end
  return try
end
0 голосов
/ 12 сентября 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...