2010-04-08 10 views
20

Ho due array di Ruby e ho bisogno di vedere se hanno valori in comune. Potrei semplicemente scorrere tutti i valori di un array e includere?() Sull'altro, ma sono sicuro che c'è un modo migliore. Che cos'è? (Gli array tengono entrambe le stringhe.)Come posso verificare se un array di Ruby include uno dei vari valori?

Grazie.

+0

Ti importa quali sono gli elementi che ha in comune? – Levi

+0

No. Tutto quello che voglio sapere è se i due hanno elementi in comune. – Colen

risposta

65

loro Set intersect:

a1 & a2 

Ecco un esempio:

> a1 = [ 'foo', 'bar' ] 
> a2 = [ 'bar', 'baz' ] 
> a1 & a2 
=> ["bar"] 
> !(a1 & a2).empty? # Returns true if there are any elements in common 
=> true 
+3

beh, l'OP vuole "controllare", quindi un risultato booleano sarebbe una soluzione migliore:! (A1 & a2) .empty? – tokland

+4

Vorrei andare con (a1 e a2) .any? invece di! (a1 & a2) .empty? – rilla

+1

@rilla 'any?' Funziona in questo caso, ma non quando si ha a che fare con valori 'false' e' nil': '[nil, false] .any? # => falso'. – Stefan

6

Qualsiasi valore in comune? è possibile utilizzare l'operatore di intersezione: &

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

Se siete alla ricerca di un incrocio pieno però (con i duplicati) il problema è più complesso si trova già un overflow dello stack qui: How to return a Ruby array intersection with duplicate elements? (problem with bigrams in Dice Coefficient)

o un rapido snippet che definisce "real_intersection" e convalida il seguente 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 
3

L'utilizzo dell'intersezione è piacevole, ma è inefficiente. Userei "qualsiasi?" sul primo array (in modo che l'iterazione si interrompa quando uno degli elementi viene trovato nel secondo array). Inoltre, l'utilizzo di un Set sul secondo array renderà veloci i controlli di appartenenza. cioè .:

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

Il benchmarking mostra che questo è 1.5-2 volte più veloce del (più esteticamente gradevole) metodo di intersezione impostato, in base al confronto tra valore semplice e confronto oggetto. Imposta l'intersezione usando "any?" Invece di "empty?", Come suggerito in un commento sopra, varia leggermente, ma non cambia il risultato. (Considerando in modo rigoroso le prestazioni, e come previsto dal momento che "any?" Si trova nella prima partita.) –

0

Prova questa

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