2014-05-09 18 views
6

voglio ottenere l'unione/intersecano/differenza di due array di hash per esempio:Come ottenere l'unione/intersezione/differenza di due array di hash e ignorare alcune chiavi

array1 = [{:name =>'Guy1', :age => 45},{:name =>'Guy2', :age => 45}] 
array2 = [{:name =>'Guy1', :age => 45},{:name =>'Guy3', :age => 45}] 

...

p array1 - array2 

=> [{:name=>"Guy2", :age=>45}] 


p array2 - array1 
=> [{:name=>"Guy3", :age=>45}] 


p array1 | array2 
=> [{:name=>"Guy1", :age=>45}, {:name=>"Guy2", :age=>45}, {:name=>"Guy3", :age=>45}] 

tuttavia quando voglio mettere a confronto solo in base ai nomi e ignorare i secoli senza bisogno di rimuoverli dalle hash per esempio:

array1 = [{:name =>'Guy1', :age => 45},{:name =>'Guy2', :age => 45}] 
array2 = [{:name =>'Guy1', :age => 46},{:name =>'Guy3', :age => 45}] 

In questo caso non sto ottenendo i risultati che voglio b/c le età sono diverse.

array1 - array2 

=> [{:name=>"Guy1", :age=>45}, {:name=>"Guy2", :age=>45}] 

array2 - array1 
=> [{:name=>"Guy1", :age=>46}, {:name=>"Guy3", :age=>45}] 

array1 | array2 
=> [{:name=>"Guy1", :age=>45}, {:name=>"Guy2", :age=>45}, {:name=>"Guy1", :age=>46}, {:name=>"Guy3", :age=>45}] 

C'è un modo per ottenere l'unione/intersezione/differenza e ignorare la chiave di età?

edit: per un esempio migliore:

array1 = [{:name =>'Guy1', :age => 45},{:name =>'Guy2', :age => 45}] 
array2 = [{:name =>'Guy1'},{:name =>'Guy3'}] 

p array1 - array2 
p array2 - array1 
p array1 | array2 
p array1 & array2 

Grazie in anticipo per l'aiuto!

+0

Qual è il risultato atteso, soprattutto quale valore per ': age'? – sawa

+0

Il risultato atteso sarebbe come se il ': age' non esistesse. –

+0

il risultato atteso sarebbe lo stesso del primo esempio. –

risposta

5

Ecco un modo rapido e sporco di ottenere l'unione:

(array1 + array2).uniq{|a| a[:name]} 

Tuttavia, mi sento di raccomandare creare la propria sottoclasse di Hash in modo da poter ignorare tranquillamente eql? come sottolinea Cary Swoveland è ciò che il set-like gli operatori si affidano a. Si noti inoltre che è necessario limitare il metodo hash per fornire solo la funzione di hashing sul campo del nome.

class Guy < Hash 

    def eql?(other_hash) 
    self[:name] == other_hash[:name] 
    end 

    def hash 
    self[:name].hash 
    end 

end 

Poi questi Guy oggetti funzioneranno in tutte le operazioni di set:

array1 = [ Guy[name:'Guy1', age: 45], Guy[name:'Guy2', age: 45] ] 
array2 = [ Guy[name:'Guy1', age: 46], Guy[name:'Guy3', age: 45] ] 

array1 - array2 
#=> [{:name=>"Guy2", :age=>45}] 

array2 - array1 
#=> [{:name=>"Guy3", :age=>45}] 

array1 | array2 
#=> [{:name=>"Guy1", :age=>45}, {:name=>"Guy2", :age=>45}, {:name=>"Guy3", :age=> 
45}] 

array1 & array2 
#=> [{:name=>"Guy1", :age=>45}] 
+0

Anche la sottoclasse può essere pericolosa, ma, sì, è meglio che manipolare con la classe 'Hash'. Se 'f' e' g' sono oggetti 'Guy', si potrebbe inavvertitamente usare' f.eql? (G) 'quando' f == g' era inteso (cambiare 'eql?' Non influenza '='), che potrebbe risultare in un bug abbastanza cattivo. –

0

Per la differenza:

diff_arr = array1.map{|a| a[:name]} - array2.map{|a| a[:name]} 

diff_arr.map{|a| array1.map{|s| s if s[:name] == a }}.flatten.compact 

Intersezione

intersec_arr = array1.map{|a| a[:name]} & array2.map{|a| a[:name]} 

intersec_ar.map{|a| array1.map{|s| s if s[:name] == a }}.flatten.compact 
0

Tutti e tre classe Array metodi di riferimento utilizzare Hash#eql? per confrontare due elementi che sono entrambi gli hash. (Hash#eql? controlla se gli hash per i due hash sono uguali.) Pertanto, è necessario ridefinire (temporaneamente) solo Hash#eql?.

array1 = [{:name =>'Guy1', :age => 45}, {:name =>'Guy2', :age => 45}] 
array2 = [{:name =>'Guy1'},    {:name =>'Guy3'}] 

class Hash 
    alias old_eql? eql? 
    def eql?(h) self[:name] == h[:name] end 
end 

array1 - array2 
    #=> [{:name=>"Guy2", :age=>45}] 
array2 - array1 
    #=> [{:name=>"Guy3"}] 
array1 | array2 
    #=> [{:name=>"Guy1", :age=>45}, {:name=>"Guy2", :age=>45}, {:name=>"Guy3"}] 
array1 & array2 
    #=> [{:name=>"Guy1", :age=>45}] 

class Hash 
    alias eql? old_eql? # Restore eql? 
    undef_method :old_eql? 
end 

Si noti che questo è uno di quei pochi casi in cui self deve essere esplicita. Se abbiamo scritto:

[:name] == h[:name] 

invece di:

self[:name] == h[:name] 

Rubino assumerebbe che stiamo confrontando l'array [:name] con h[:name].

+2

Non consiglierei questo approccio a causa del patching scimmia di una classe core 'Hash'. Tuttavia, se l'OP dovesse creare una nuova classe, ad es. 'class Guys

+0

Inoltre, questo non funziona. Penso che tu stia dimenticando qualcosa (vedi la mia risposta per un suggerimento). –

+0

Buoni punti, @Mark. Principalmente ho proposto questa soluzione perché pensavo che avesse un valore educativo. Durante la scrittura sapevo che stavo pattinando su un ghiaccio sottile; Avrei dovuto almeno aggiungere alcune parole di avvertimento. Anche la sottoclasse di 'Hash' può essere pericolosa - come menzionerò in un commento alla tua risposta - ma meno che armeggiare con' Hash'. Tratterò il tuo secondo punto in un commento separato. –