How to get union / intersection / difference from two hash arrays and ignore some keys

I want to get union / intersection / difference from two hash arrays, for example:

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}] 

however, when I want to compare only based on names and ignore ages without removing them from hashes, for example:

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

In this case, I do not get the results that I need b / c, the ages are different.

 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}] 

Is there a way to get union / intersection / difference and ignore the age key?

edit: for a better example:

 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 

Thanks in advance for your help!

+6
source share
3 answers

Here's a quick and dirty way to get the join:

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

However, I would recommend creating your own subclass of Hash so that you can safely override eql? , as Carey Charlotte points out, on which operators like set rely. Note that you also need to limit the Hash method to only providing a hash function in the name field.

 class Guy < Hash def eql?(other_hash) self[:name] == other_hash[:name] end def hash self[:name].hash end end 

Then these Guy objects will work in all given operations:

 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}] 
+5
source

For the difference:

 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 

Intersection

 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
source

All three classes of Array methods reference the use of Hash # eql? to compare two elements that are hashes. ( Hash#eql? Checks if the hash codes for the two hashes are equal.) So we need to (temporarily) override 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 

Note that this is one of those situations where self should be explicit. If we wrote:

 [:name] == h[:name] 

instead:

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

Ruby would suggest that we compare the array [:name] with h[:name] .

0
source

All Articles