Techioz Blog

Rubyのネストされたハッシュ内の同じキーの値を平均する

概要

次のようなネストされたハッシュがあります。

    main_hash = [[
  {"a"=>{:x=>70.1}, "b"=>{:x=>97.17}, "c"=>{:x=>97.25}}], 
  [{"a"=>{:x=>70.05}, "b"=>{:x=>97.17}, "c"=>{:x=>97.25}}], 
  [{"a"=>{:x=>70.12}, "b"=>{:x=>97.17}, "c"=>{:x=>97.25}}], 
  [{"a"=>{:x=>70.08}, "b"=>{:x=>97.17}, "c"=>{:x=>97.23}}]]

最終的に同じキー a…f を持つ 1 つのハッシュになり、各キーには元のハッシュの対応する値の平均が含まれるようにしたいと考えています。

return_hash = {"a" => average of all the values[:x] corresponding to "a" in each hash, 
"b" => average of all the values[:x] corresponding to "b" in each hash,
"c" => average of all the values[:x] corresponding to "c" in each hash}

mergeとmapを使用しようとしましたが、この結果を達成するために使用できるより効率的なブロックはありますか?

これまでのところ、私はこのルートをたどってきました。

          keys = []
          values = []
          divider = main_hash.size 
          iteration = main.first.first.size
          iteration.times do
              sum = 0
              keys << main_hash.first[0].first[0]
              main_hash.each do |f|
                f.each do |g|
                  sum += g.dig(cores.keys.first, :x)
                  g.shift
                end
              end
              values << ((sum / divider).round(2))
          ret_hash = keys.zip(values).to_h

解決策

基本的にここでできることは、配列をフラット化してサブ配列を削除し、各ハッシュ キーのすべての値を配列に収集することです。

次に、各配列を平均します。

# loop through the outer array and return a hash
main_hash.flatten.each_with_object({}) do |hash, memo|
  # loop over the keys and values of the hash
  hash.each_pair do |name, value|
    memo[name] ||= []
    memo[name].push(value.dig(:x))
  end
end # {"a"=>[70.1, 70.05, 70.12, 70.08], ... }
  # loop across the values and convert the array to an average
  .transform_values { |ary| ary.sum / ary.length } 
  # {"a"=>70.0875, "b"=>97.17, "c"=>97.245}

または、Hash#merge! を使用することもできます。

main_hash.flatten.each_with_object({}) do |hash, memo|
  memo.merge!(hash) do |key, old, new|
   (old.is_a?(Hash) ? old.values_at(:x) : old).push(new[:x])
  end
end.transform_values { |ary| ary.sum / ary.length }