Comparing two similar hashes in ruby

I am using ruby ​​1.8.7, and I need to compare the two hashes that I have, which are essentially attributes of the model. Hash A is smaller than Hash B, and Hash B has all the attributes of hash A, as well as some additional attributes that I don't need. My main goal is to check if the elements of A coincide with the corresponding elements of B. So for example

@hash_a = {:cat => 'Bubby', :dog => 'Gizmo'} @hash_b = {:cat => 'Bubby', :dog => 'Gizmo', :lion => 'Simba'} @hash_a == @hash_b #=> true 

Now it’s a little more complicated, because the fields do not completely coincide, although they refer to the same piece of information

 @hash_a = {:cats_name => 'Bubby', :dog => 'Gizmo'} @hash_b = {:cat => 'Bubby', :dog => 'Gizmo', :lion => 'Simba'} @hash_a == @hash_b #=> true 

What I'm working on is a process that compares two matching elements, updates it if the fields have been changed, and only if they have changed. Or creates a new element if it cannot find the corresponding element. Changing the names of the hash itself is not an option. Currently, I'm just comparing each field in a private method to make sure they are equal.

 return hash_a[:cat] == hash_b[:cats_name] && hash_a[:dog] == hash_b[:dog] 

I feel that there must be a better way, I am looking for something faster and more elegant than this.

+4
source share
4 answers

If you convert hashes to an array, you can compare them as follows.

 @hash_a.to_a == (@hash_a.to_a & @hash_b.to_a) 

You can also hide this code behind the method in the hash class if you want:

 class Hash def diff_equal(other) to_a == (to_a & other.to_a) end end 

Then use it as @hash_a.diff_equal(@hash_b) . If you chose this path, you can check if another hash is or is responding to the to_a method.

+3
source

Heh, if you really want fast and elegant, here you are:

 (a = @hash_a.values; (a & @hash_b.values) == a) 

There are certain obvious limitations ...

+1
source

Here's how I would do it:

 def eql hash1, hash2, rewire = {} map = Hash.new {|h, key| rewire[key] || key} !hash1.any? do |key, val| hash2[map[key]] != val end end hash_a = {:cats_name => 'Bubby', :dog => 'Gizmo'} hash_b = {:cat => 'Bubby', :dog => 'Gizmo', :lion => 'Simba'} p eql(hash_a, hash_b) #=> false hash_a = {:cats_name => 'Bubby', :dog => 'Gizmo'} hash_b = {:cat => 'Bubby', :dog => 'Gizmo', :lion => 'Simba'} p eql(hash_a, hash_b, :cats_name => :cat) #=> true hash_a = {:cat => 'Bubby', :dog => 'Gizmo'} hash_b = {:cat => 'Bubby', :dog => 'Gizmo', :lion => 'Simba'} p eql(hash_a, hash_b) #=> true hash_a = {:cat => 'Bubby', :dog => 'Gizmo', :fish => "Wanda"} hash_b = {:cat => 'Bubby', :dog => 'Gizmo', :lion => 'Simba'} p eql(hash_a, hash_b) #=> false 

Not too long and it seems to work the way you want it :)

+1
source

One possibility is to first reassign the keys of a single hash, and then perform a given subset operation:

 require 'set' def remap_keys(hash, key_map) hash.inject({}) do |acc, pair| key, value = pair remapped_key = key_map[key] || key acc[remapped_key] = value acc end end def hash_subset?(a, b) set_a = Set.new(a) set_b = Set.new(b) set_a.subset?(set_b) end hash_a = {:cats_name => 'Bubby', :dog => 'Gizmo'} hash_b = {:cat => 'Bubby', :dog => 'Gizmo', :lion => 'Simba'} puts hash_subset?(remap_keys(hash_a, {:cats_name => :cat}), hash_b) 

However, I am sure that there are more effective ways to do this. More than one way to crop a :cat , eh ?!

0
source

All Articles