Подтвердить что ты не робот

Array.include? несколько значений

[2, 6, 13, 99, 27].include?(2) хорошо работает для проверки того, содержит ли массив одно значение. Но что, если я хочу проверить, содержит ли массив какой-либо из списка из нескольких значений? Есть ли более короткий путь, чем делать Array.include?(a) or Array.include?(b) or Array.include?(c) ...?

4b9b3361

Ответ 1

Вы можете взять пересечение двух массивов и посмотреть, не пусто ли это:

([2, 6, 13, 99, 27] & [2, 6]).any?

Ответ 2

Вы можете использовать метод Enumerable # any? с блоком кода для проверки на включение нескольких значений. Например, чтобы проверить либо 6, либо 13:

[2, 6, 13, 99, 27].any? { |i| [6, 13].include? i }

Ответ 3

Мне было интересно увидеть, как эти различные подходы сравниваются по производительности, а не только по проблеме, но больше для общих сравнений массива vs set intersection, array vs set include? и include? vs index для массивов. Я отредактирую, чтобы добавить другие предложенные методы, и дайте мне знать, хотите ли вы увидеть разные контрольные параметры.

Я хотел бы, чтобы больше было проведено сравнительное тестирование SO-ответов. Это не сложно или занимает много времени, и это может дать полезную информацию. Я нахожу, что большую часть времени готовят тестовые примеры. Заметьте, что я поставил методы для тестирования в модуле, поэтому, если нужно провести сравнительный анализ другого метода, мне нужно только добавить этот метод к модулю.

Сравнение методов

module Methods
  require 'set'
  def august(a,b)    (a&b).any? end
  def gnome_inc(a,b) a.any? { |i| b.include? i } end
  def gnome_ndx(a,b) a.any? { |i| b.index i } end
  def gnome_set(a,b) bs=b.to_set; a.any? { |i| bs.include? i } end
  def vii_stud(a,b)  as, bs = Set.new(a), Set.new(b); as.intersect?(bs) end
end

include Methods
@methods = Methods.instance_methods(false)
  #=> [:august, :gnome_inc, :gnome_ndx, :gnome_set, :vii_stud]

Данные тестирования

def test_data(n,m,c,r)
  # n: nbr of elements in a
  # m: nbr of elements in b
  # c: nbr of elements common to a & b
  # r: repetitions
  r.times.each_with_object([]) { |_,a|
    a << [n.times.to_a.shuffle, [*(n-c..n-c-1+m)].shuffle] }
end

d = test_data(10,4,2,2)
  #=> [[[7, 8, 0, 3, 2, 9, 1, 6, 5, 4], [11, 10,  9, 8]], 
  #    [[2, 6, 3, 4, 7, 8, 0, 9, 1, 5], [ 9, 11, 10, 8]]]
# Before `shuffle`, each of the two elements is:
  #=> [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [8, 9, 10, 11]] 

def compute(d, m)
  d.each_with_object([]) { |(a,b),arr| arr << send(m, a, b) }
end  

compute(d, :august)
 #=> [true, true]

Подтвердить методы возвращают те же значения

d = test_data(1000,100,10,3)
r0 = compute(d, @methods.first) 
puts @methods[1..-1].all? { |m| r0 == compute(d, m) }
  #=> true

Контрольный код

require 'benchmark'

@indent = methods.map { |m| m.to_s.size }.max

def test(n, m, c, r, msg)
  puts "\n#{msg}"
  puts "n = #{n}, m = #{m}, overlap = #{c}, reps = #{r}"
  d = test_data(n, m, c, r)
  Benchmark.bm(@indent) do |bm|
    @methods.each do |m|
      bm.report m.to_s do
        compute(d, m)
      end
    end
  end
end

Испытания

n = 100_000
m = 1000
test(n, m,    0,  1, "Zero overlap")
test(n, m, 1000,  1, "Complete overlap")
test(n, m,    1, 20, "Overlap of 1")
test(n, m,    5, 20, "Overlap of 5")
test(n, m,   10, 20, "Overlap of 10")
test(n, m,   20, 20, "Overlap of 20")
test(n, m,   50, 20, "Overlap of 50")
test(n, m,  100, 20, "Overlap of 100")

Zero overlap
n = 100000, m = 1000, overlap = 0, reps = 1
                                 user     system      total        real
august                       0.010000   0.000000   0.010000 (  0.005491)
gnome_inc                    4.480000   0.010000   4.490000 (  4.500531)
gnome_ndx                    0.810000   0.000000   0.810000 (  0.822412)
gnome_set                    0.030000   0.000000   0.030000 (  0.031668)
vii_stud                     0.080000   0.010000   0.090000 (  0.084283)

Complete overlap
n = 100000, m = 1000, overlap = 1000, reps = 1
                                 user     system      total        real
august                       0.000000   0.000000   0.000000 (  0.005841)
gnome_inc                    0.010000   0.000000   0.010000 (  0.002521)
gnome_ndx                    0.000000   0.000000   0.000000 (  0.000350)
gnome_set                    0.000000   0.000000   0.000000 (  0.000655)
vii_stud                     0.090000   0.000000   0.090000 (  0.097850)

Overlap of 1
n = 100000, m = 1000, overlap = 1, reps = 20
                                 user     system      total        real
august                       0.110000   0.000000   0.110000 (  0.116276)
gnome_inc                   61.790000   0.100000  61.890000 ( 62.058320)
gnome_ndx                   10.100000   0.020000  10.120000 ( 10.144649)
gnome_set                    0.360000   0.000000   0.360000 (  0.357878)
vii_stud                     1.450000   0.050000   1.500000 (  1.501705)

Overlap of 5
n = 100000, m = 1000, overlap = 5, reps = 20
                                 user     system      total        real
august                       0.110000   0.000000   0.110000 (  0.113747)
gnome_inc                   16.550000   0.050000  16.600000 ( 16.728505)
gnome_ndx                    2.470000   0.000000   2.470000 (  2.475111)
gnome_set                    0.100000   0.000000   0.100000 (  0.099874)
vii_stud                     1.630000   0.060000   1.690000 (  1.703650)

Overlap of 10
n = 100000, m = 1000, overlap = 10, reps = 20
                                 user     system      total        real
august                       0.110000   0.000000   0.110000 (  0.112674)
gnome_inc                   10.090000   0.020000  10.110000 ( 10.131339)
gnome_ndx                    1.470000   0.000000   1.470000 (  1.478400)
gnome_set                    0.060000   0.000000   0.060000 (  0.062762)
vii_stud                     1.430000   0.050000   1.480000 (  1.476961)

Overlap of 20
n = 100000, m = 1000, overlap = 20, reps = 20
                                 user     system      total        real
august                       0.100000   0.000000   0.100000 (  0.108350)
gnome_inc                    4.020000   0.000000   4.020000 (  4.026290)
gnome_ndx                    0.660000   0.010000   0.670000 (  0.663001)
gnome_set                    0.030000   0.000000   0.030000 (  0.024606)
vii_stud                     1.380000   0.050000   1.430000 (  1.437340)

Overlap of 50
n = 100000, m = 1000, overlap = 50, reps = 20
                                 user     system      total        real
august                       0.120000   0.000000   0.120000 (  0.121278)
gnome_inc                    2.170000   0.000000   2.170000 (  2.236737)
gnome_ndx                    0.310000   0.000000   0.310000 (  0.308336)
gnome_set                    0.020000   0.000000   0.020000 (  0.015326)
vii_stud                     1.220000   0.040000   1.260000 (  1.259828)

Overlap of 100
n = 100000, m = 1000, overlap = 100, reps = 20
                                 user     system      total        real
august                       0.110000   0.000000   0.110000 (  0.112739)
gnome_inc                    0.720000   0.000000   0.720000 (  0.712265)
gnome_ndx                    0.100000   0.000000   0.100000 (  0.105420)
gnome_set                    0.010000   0.000000   0.010000 (  0.009398)
vii_stud                     1.400000   0.050000   1.450000 (  1.447110)

Ответ 4

Простой способ:

([2, 6] - [2, 6, 13, 99, 27]).empty?

Ответ 5

require 'set'

master = Set.new [2, 6, 13, 99, 27]
data = Set.new [27, -3, -4]
#puts data.subset?(master) ? 'yes' : 'no'  #per @meager comment
puts data.intersect?(master) ? 'yes' : 'no'

--output:--
yes

Ответ 6

Это работает - если какое-либо значение соответствует:

arr = [2, 6, 13, 99, 27]
if (arr - [2, 6]).size < arr.size
 puts 'element match found'
else
 puts 'element not found'
end

Ответ 7

Один из моих любимых способов сделать это в спецификациях - преобразовать массив и значение в Set и проверить его через #superset? & #subset? методы.

Например:

[1, 2, 3, 4, 5].to_set.superset?([1, 2, 3].to_set) # true
[1, 2, 3].to_set.subset?([1, 2, 3, 4, 5].to_set)   # true
[1, 2].to_set.subset?([1, 2].to_set)               # true
[1, 2].to_set.superset?([1, 2].to_set)             # true

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

[1, 1, 1, 1, 1].to_set.subset? [1, 2].to_set       # true

Чтобы не вызывать .to_set каждый раз, я обычно определяю для этого соответствие:

it 'returns array of "shown" proposals' do
  expect(body_parsed.first.keys).to be_subset_of(hidden_prop_attrs)
end

По моему скромному мнению, быть надмножеством или подмножеством просто читабельнее, чем делать:

([1, 2, 3] & [1, 2]).any?

Однако преобразование массива в набор может быть менее производительным. Компромиссы ¯\_ (ツ) _/¯

Ответ 8

Я расширяю массив с этими:

class Array

  def include_exactly?(values)
    self.include_all?(values) && (self.length == values.length)
  end
  def include_any?(values)
    values.any? {|value| self.include?(value)}
  end
  def include_all?(values)
    values.all? {|value| self.include?(value)}
  end
  def exclude_all?(values)
    values.all? {|value| self.exclude?(value)}
  end

end