How can I check if a Ruby array includes one of several values?
Asked Answered
B

5

45

I have two Ruby arrays, and I need to see if they have any values in common. I could just loop through each of the values in one array and do include?() on the other, but I'm sure there's a better way. What is it? (The arrays both hold strings.)

Thanks.

Bubble answered 8/4, 2010 at 22:30 Comment(2)
Do you care what elements it has in common?Evaginate
Nope. All I want to know is if the two have any elements in common at all.Bubble
P
101

Set intersect them:

a1 & a2

Here's an example:

> a1 = [ 'foo', 'bar' ]
> a2 = [ 'bar', 'baz' ]
> a1 & a2
=> ["bar"]
> !(a1 & a2).empty? # Returns true if there are any elements in common
=> true
Pasargadae answered 8/4, 2010 at 22:32 Comment(6)
well, the OP wants "to check", so a boolean result would be a better fit: !(a1 & a2).empty?Complect
I'd go with (a1 & a2).any? instead of !(a1 & a2).empty?Data
@Data any? works in this case, but not when dealing with false and nil values: [nil, false].any? #=> false.Horotelic
is there a neater way to do !(a1 & a2).empty??Misshapen
@DavidWest yes, (a1 & a2).present?.Fortyniner
@Maurice note that #present? requires ActiveSupport, so it isn't a straight Ruby solution.Fahy
F
8

Any value in common ? you can use the intersection operator : &

[ 1, 1, 3, 5 ] & [ 1, 2, 3 ]   #=> [ 1, 3 ]

If you are looking for a full intersection however (with duplicates) the problem is more complex there is already a stack overflow here : How to return a Ruby array intersection with duplicate elements? (problem with bigrams in Dice Coefficient)

Or a quick snippet which defines "real_intersection" and validates the following test

class ArrayIntersectionTests < Test::Unit::TestCase    
  def test_real_array_intersection
    assert_equal [2], [2, 2, 2, 3, 7, 13, 49] & [2, 2, 2, 5, 11, 107]
    assert_equal [2, 2, 2], [2, 2, 2, 3, 7, 13, 49].real_intersection([2, 2, 2, 5, 11, 107])
    assert_equal ['a', 'c'], ['a', 'b', 'a', 'c'] & ['a', 'c', 'a', 'd']
    assert_equal ['a', 'a', 'c'], ['a', 'b', 'a', 'c'].real_intersection(['a', 'c', 'a', 'd'])
  end
end
Furlough answered 8/4, 2010 at 22:37 Comment(0)
S
8

Using intersection looks nice, but it is inefficient. I would use "any?" on the first array (so that iteration stops when one of the elements is found in the second array). Also, using a Set on the second array will make membership checks fast. i.e.:

a = [:a, :b, :c, :d]
b = Set.new([:c, :d, :e, :f])
c = [:a, :b, :g, :h]

# Do a and b have at least a common value?
a.any? {|item| b.include? item}
# true

# Do c and b have at least a common value?
c.any? {|item| b.include? item}
#false
Sergu answered 9/11, 2015 at 18:40 Comment(1)
Benchmarking shows this to be 1.5-2x faster than the (more aesthetically pleasing) set intersection method, depending on simple value versus object attribute comparison. Set intersection using any? instead of empty?, as suggested in a comment above, varied slightly, but didn't change the outcome. (Strictly considering performance, and as expected since any? bails on the first match.)Endoblast
B
6

Array#intersect? (Ruby 3.1+)

Starting from Ruby 3.1, there is a new Array#intersect? method, which checks whether two arrays have at least one element in common.

Here is an example:

a = [1, 2, 3]
b = [3, 4, 5]
c = [7, 8, 9]

# 3 is the common element
a.intersect?(b)
# => true

# No common elements
a.intersect?(c)
# => false

Also, Array#intersect? can be much faster than alternatives since it avoids creating an intermediate array, returns true as soon as it finds a common element, it is implemented in C.

Sources:

Buddy answered 11/5, 2021 at 19:13 Comment(0)
O
0

Try this

a1 = [ 'foo', 'bar' ] 
a2 = [ 'bar', 'baz' ]
a1-a2 != a1
true
Overlong answered 18/4, 2017 at 13:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.