# Merging two hashes with the mean values of each key if both hashes exist

There are two methods, hash1 and hash2, that return hashes or nil. The hashes have the same keys. I want to merge them with their mean value only if both methods have hashes.

(I added Array#mean to calculate the average value of an array).

def merged_hashes
return if hash1.nil? && hash2.nil?
return hash2 if hash1.nil?
return hash1 if hash2.nil?
hash1.merge(hash2) do |key, v1, v2|
[v1, v2].compact.mean
end
end


Is there more succinct way to write this?

• – Marc B May 30 '16 at 19:04
• Ah, the hashes have same keys. I added the info in my question. – ironsand May 30 '16 at 19:08
• You are right. I thought try covers that condition, but it isn't. I'll edit the question. – ironsand May 30 '16 at 19:30
• are you require to retunr nil when both hashes are absent? – Малъ Скрылевъ May 30 '16 at 19:44
• Yes, I want to get nil value when both are nil. – ironsand May 30 '16 at 19:45

There are many ways to do this, but all have two common elements. Firstly, since hash1 and hash2 are methods, you want to only call those methods once, which means saving the return values in variables:

h1 = hash1
h2 = hash2


Secondly, there are four possible courses of action, depending on the values of h1 and h2. You may wish to represent these with a case statement:

def merged_hashes(hash1,hash2)
h1 = hash1
h2 = hash2
case [h1.is_a?(Hash), h2.is_a?(Hash)]
when [false, false] then nil
when [false, true]  then h2
when [true, false]  then h1
else compute_avg(h1, h2)
end
end


Note that it is necessary to compute both h1 and h2 even if the first one computed is found to not be a hash.

All that remains is to construct the method compute_avg. Here are three ways that might be done:

Merge h1 and h2, using the form of Hash#merge that employs a block to determine the values of keys that are present in both hashes being merged, which here is all keys.

compute_avg(h1,h2)
h1.merge(h2) { |_, v1, v2| [v1, v2].compact.mean }
end


You used merge in this way in your solution.

Extract values of h2 and send Enumerable#each_with_object to h1 to build the desired hash

compute_avg(h1,h2)
values2 = h2.values
h1.each_with_object({}) { |(k,v1),h| h[k] = [v1, values2.shift].compact.mean }
end


Extract the values of h1 and h2, compute their averages, element-by-element, then construct the hash to be returned

compute_avg(h1,h2)
keys = h1.keys
averages = { h1.values.zip(h2.values).map { |x1,x2| [x1, x2].compact.mean  } }
keys.zip(averages).to_h
end