Techioz Blog

Rubyで値の繰り返しを合計し、キーの2つの値に加算する方法は?

概要

ディレクトリ上の拡張子の種類ごとに 1 つのキーを持つハッシュを作成しようとしています。すべてのキーに 2 つの値を追加します。拡張子が繰り返される回数と、その拡張子を持つすべてのファイルの合計サイズです。

これに似たもの:

{“.md” => {“ext_reps” => 6、“ext_size_sum” => 2350}、“.txt” => {“ext_reps” => 3、“ext_size_sum” => 1300}}

しかし、私はこのステップで行き詰まっています:

hash = Hash.new{|hsh,key| hsh[key] = {}}
ext_reps = 0
ext_size_sum = 0

Dir.glob("/home/computer/Desktop/**/*.*").each do |file|
  hash[File.extname(file)].store "ext_reps", ext_reps
  hash[File.extname(file)].store "ext_size_sum", ext_size_sum 
end

p hash

この結果:

{“.md” => {“ext_reps” => 0, “ext_size_sum” => 0}, “.txt” => {“ext_reps” => 0, “ext_size_sum” => 0}}

そして、ext_repsとext_siz_sumをインクリメントする方法が見つかりません

ありがとう

解決策

描画されたファイル名の拡張子とファイルサイズが次のとおりであるとします。

files = [{ ext: 'a', size: 10 },
         { ext: 'b', size: 20 },
         { ext: 'a', size: 30 },
         { ext: 'c', size: 40 },
         { ext: 'b', size: 50 },
         { ext: 'a', size: 60 }]

Hash#group_by と Hash#transform_values を使用できます。

files.group_by { |h| h[:ext] }.
      transform_values do |arr|
        { "ext_reps"=>arr.size, "ext_size_sum"=>arr.sum { |h| h[:size] } }
      end
        #=> {"a"=>{"ext_reps"=>3, "ext_size_sum"=>100},
        #    "b"=>{"ext_reps"=>2, "ext_size_sum"=>70},
        #    "c"=>{"ext_reps"=>1, "ext_size_sum"=>40}}

なお、最初の計算は以下の通りです。

files.group_by { |h| h[:ext] }
  #=> {"a"=>[{:ext=>"a", :size=>10}, {:ext=>"a", :size=>30},
  #          {:ext=>"a", :size=>60}],
  #    "b"=>[{:ext=>"b", :size=>20}, {:ext=>"b", :size=>50}],
  #    "c"=>[{:ext=>"c", :size=>40}]}

もう 1 つの方法は、マージされる両方のハッシュに存在するキーの値を計算するブロックを使用する Hash#update (別名 Hash#merge!) および Hash#merge の形式を使用することです。 (hにキーkがない場合、キーkを持つキーと値のペアが構築中のハッシュ(h)にマージされるとき、Rubyはそのブロックを参照しません。)

マージされるハッシュの共通キーの値を返すブロックの 3 つのパラメーターの説明については、ドキュメントを参照してください。

files.each_with_object({}) do |g,h|
   h.update(g[:ext]=>{"ext_reps"=>1, "ext_size_sum"=>g[:size]}) do |_k,o,n|
     o.merge(n) { |_kk, oo, nn| oo + nn }
   end
end
  #=> {"a"=>{"ext_reps"=>3, "ext_size_sum"=>100},
  #    "b"=>{"ext_reps"=>2, "ext_size_sum"=>70},
  #    "c"=>{"ext_reps"=>1, "ext_size_sum"=>40}}

「外側」ハッシュと「内側」ハッシュの共通キー (それぞれ _k と _kk) の名前は、ブロック計算で使用されないことを読者に示すためにアンダースコアで始まる名前を選択しました。これは一般的な慣例です。

このアプローチでは、group_by によって作成されるものと同様の一時ハッシュの作成が回避されるため、最初のアプローチよりも使用するメモリが少なくなる傾向があることに注意してください。