環境
Ruby 2.6
Ubuntu20.4
はじめに
ハッシュの特定のキーに対して、同一値のグループで、値を合計する処理を行います。この表現だけでは、何のことかよく分からないかと思います。自分も上手く表現することができません。
エンジニア向けに、SQL文を使って、1言で表現すると次のようになります。
Select Sum(金額) From HASH Group by コード
ハッシュに対してGroup byのSQL文を発行して、結果を取得するようなことを行います。
条件
下記のハッシュを利用します。ハッシュは、キー(cd)で昇順にソートされていることが前提になります。
ary = [{:id=>1, :cd=>1, :kei=>100},
{:id=>2, :cd=>1, :kei=>110},
{:id=>3, :cd=>2, :kei=>120},
{:id=>4, :cd=>2, :kei=>130},
{:id=>5, :cd=>3, :kei=>140},
{:id=>6, :cd=>3, :kei=>150}]
やり方(その1)
まず最初は、eachメソッドを使う方法です。このやり方が、一番シンプルだと個人的に思います。ただし、eachメソッドを使うと、ハッシュの先頭1行と、最終1行がeachのブロック内で取得できないため、eachブロックの外で取得することになります。
# 初期値
kei = 0; wcd = 0
# 先頭の1行目だけを取得
ary.each { |val| wcd = val[:cd]; break }
ary.each do |val|
if (wcd == val[:cd])
else
puts "cd= #{wcd} kei= #{kei}"
kei = 0
end
kei += val[:kei].to_i
wcd = val[:cd]
end
# 最終行の処理
puts "cd= #{wcd} kei= #{kei}"
処理結果です。
cd= 1 kei= 210
cd= 2 kei= 250
cd= 3 kei= 290
やり方(その2)
2つ目は、each_consメソッドを使う方法です。each_consメソッドは、ハッシュの中身を、2行連続で取得することができるメソッドです。書き始める前は、eachメソッドを使うよりも、シンプルに記述できそうな印象を持ちましたが、書いてみると、それ程でもありませんでした。パッと見では、eachメソッドを使った方が、まだ、可読性は上だと思います。
# 初期値
kei = 0; wcd = 0
# 先頭の1行目だけを取得
ary.each { |val| kei = val[:kei].to_i; break }
ary.each_cons(2) do |prv, val|
if (prv[:cd] == val[:cd])
else
puts "cd= #{prv[:cd]} kei= #{kei}"
kei = 0
end
kei += val[:kei].to_i
wcd = val[:cd] # 最終行だけで利用
end
# 最終行の処理
puts "cd= #{wcd} kei= #{kei}"
処理結果です。
cd= 1 kei= 210
cd= 2 kei= 250
cd= 3 kei= 290
やり方(その3)
3つ目は、each_with_indexメソッドを使う方法です。each_with_indexメソッドを使うと、eachブロックの中でインデックスを取得することができます。インデックスが取得できると、先頭1行と最終1行のデータを、ブロックの中で拾うことができるようになります。
コードを見ると分かりますが、先頭1行を取得する処理を省略することができました。また、最終1行を取得する処理を、ブロックの中に収めることができました。これで、少しはスッキリしたのではないでしょうか。
# 初期値
kei = 0; wcd = 0
ary.each_with_index do |val, idx|
if (idx > 0)
if (wcd == val[:cd])
else
puts "cd= #{wcd} kei= #{kei}"
kei = 0
end
end
kei += val[:kei].to_i
wcd = val[:cd]
# 最終行の処理
if (idx == ary.size - 1 )
puts "cd= #{wcd} kei= #{kei}"
end
end
処理結果です。
cd= 1 kei= 210
cd= 2 kei= 250
cd= 3 kei= 290