2
\$\begingroup\$

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?

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

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
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.