Array.include? multiple values
[2, 6, 13, 99, 27].include?(2)
works well for checking if the array includes one value. But what if I want to check if an array includes any one from a list of multiple values? Is there a shorter way than doing Array.include?(a) or Array.include?(b) or Array.include?(c) ...
?
You could take the intersection of two arrays, and see if it's not empty:
([2, 6, 13, 99, 27] & [2, 6]).any?
You can use the Enumerable#any? method with a code block to test for inclusion of multiple values. For example, to check for either 6 or 13:
[2, 6, 13, 99, 27].any? { |i| [6, 13].include? i }
I was interested in seeing how these various approach compared in performance, not so much for the problem at hand, but more for general comparisons of array vs set intersection, array vs set include?
and include?
vs index
for arrays. I will edit to add other methods that are suggested, and let me know if you'd like to see different benchmark parameters.
I for one would like to see more benchmarking of SO answers done. It's not difficult or time-consuming, and it can provide useful insights. I find most of the time is preparing the test cases. Notice I've put the methods to be tested in a module, so if another method is to be benchmarked, I need only add that method to the module.
Methods compared
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]
Test data
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]
Confirm methods return the same values
d = test_data(1000,100,10,3)
r0 = compute(d, @methods.first)
puts @methods[1..-1].all? { |m| r0 == compute(d, m) }
#=> true
Benchmark code
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
Tests
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)