47

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.

2
  • Do you care what elements it has in common? Commented Apr 9, 2010 at 0:19
  • Nope. All I want to know is if the two have any elements in common at all. Commented Apr 13, 2010 at 20:51

5 Answers 5

102

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
Sign up to request clarification or add additional context in comments.

6 Comments

well, the OP wants "to check", so a boolean result would be a better fit: !(a1 & a2).empty?
I'd go with (a1 & a2).any? instead of !(a1 & a2).empty?
@rilla any? works in this case, but not when dealing with false and nil values: [nil, false].any? #=> false.
is there a neater way to do !(a1 & a2).empty??
@DavidWest yes, (a1 & a2).present?.
|
10

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:

Comments

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

Comments

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

1 Comment

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.)
0

Try this

a1 = [ 'foo', 'bar' ] 
a2 = [ 'bar', 'baz' ]
a1-a2 != a1
true

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.