0
0

More than 1 year has passed since last update.

eachでハッシュの値をグループ化して合計する方法

Last updated at Posted at 2022-09-06

環境

Ruby 2.6
Ubuntu20.4

はじめに

ハッシュの特定のキーに対して、同一値のグループで、値を合計する処理を行います。この表現だけでは、何のことかよく分からないかと思います。自分も上手く表現することができません。

エンジニア向けに、SQL文を使って、1言で表現すると次のようになります。

Select Sum(金額) From HASH Group by コード

ハッシュに対してGroup byのSQL文を発行して、結果を取得するようなことを行います。

条件

下記のハッシュを利用します。ハッシュは、キー(cd)で昇順にソートされていることが前提になります。

ary.rb
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ブロックの外で取得することになります。

sample.rb
#  初期値
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メソッドを使った方が、まだ、可読性は上だと思います。

sample.rb
#  初期値
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行を取得する処理を、ブロックの中に収めることができました。これで、少しはスッキリしたのではないでしょうか。

sample.rb
#  初期値
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
0
0
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0