VBA実務メモ

Dictionaryで複数項目の
重複をまとめて集計する

A列とC列の組み合わせが同じ行を探し、F列の数値を合計したい。離れた場所に同じ項目が並ぶデータを、Dictionaryで一度に集計します。

今回やりたかったのは、重複している項目をまとめ、同じ項目に対応する数値の合計を出す処理です。

元データでは、集計の基準になる項目がA列とC列にあり、合計したい数値がF列に入っています。A列とC列の両方が同じ行を一つのグループとして扱い、集計結果を別シートのA列・C列・F列へ書き出します。

i

実際の項目名やシート名は使わず、この記事では一般的な名称とサンプルデータに置き換えています。

WHY DICTIONARY

順不同の重複データをどうまとめるか

同じ項目が連続して並んでいれば、一つ前の行と比較しながら集計する方法も考えられます。しかし今回は、同じ組み合わせが離れた行にランダムに入っていました。

そこで、すでに登場した項目と合計値をセットで覚えておけるDictionaryを使います。

For~Next全行を順番に読む

元データを上から下まで確認する役割。

Dictionary既出かを判定して合計する

同じ項目が離れていても、一つの合計値にまとめる役割。

For~NextとDictionaryの二者択一ではなく、Forで読み、Dictionaryでまとめる。

COMPOSITE KEY

A列とC列を一つのキーにする

最初に見た解説では、検索する項目が一つだけでした。今回はA列とC列の二つが一致したときに同じ項目と判断したいため、二つの値を連結してDictionaryのキーにします。

A列部品A
C列工程1
Dictionaryのキー部品A[区切り]工程1
VBA
key = CStr(.Cells(i, "A").Value) & vbTab & _
      CStr(.Cells(i, "C").Value)

vbTabは、A列とC列の値を区切るために挟んでいます。たとえば「部品A+工程1」と「部品A+工程2」は、別のキーとして保存されます。

BEFORE / AFTER

離れた行にある同じ組み合わせを集計する

次の例では、「部品A・工程1」と「部品B・工程2」が離れた行に重複があります。これを右表のように行の並び順に関係なく、それぞれのF列を合算させます。

集計前元データ
A列C列F列
部品A工程13
部品B工程22
部品A工程15
部品A工程24
部品B工程26
集計後別シートへ出力
A列B列(C列)C列 (F列合計)
部品A工程18
部品B工程28
部品A工程24

CORE LOGIC

同じキーがあれば加算、なければ登録

Dictionaryを使う中心部分は、次のExistsによる分岐です。CDbl関数は、数値や数値形式の文字列を小数を含む実数として扱いたい場合に使用します。

VBA
If dict.Exists(key) Then
    dict(key) = dict(key) + CDbl(.Cells(i, "F").Value)
Else
    dict.Add key, CDbl(.Cells(i, "F").Value)
End If
dict.Exists(key)

同じA列・C列の組み合わせが、すでに登録されているか確認する。

dict(key) + 数値

登録済みなら、そのキーが持っている合計値へF列の数値を加える。

dict.Add

初めて登場した組み合わせなら、キーとF列の数値を新しく登録する。

dict.Keys

集計後、重複のないキーを一つずつ取り出して別シートへ出力する。

COMPLETE SAMPLE

処理全体のサンプル

参照設定が不要なCreateObjectを使った例です。1行目を見出し、2行目からデータが始まる想定にしています。

VBA
Option Explicit

Sub AggregateWithDictionary()

    Dim wsSource As Worksheet
    Dim wsOutput As Worksheet
    Dim dict As Object
    Dim lastRow As Long
    Dim i As Long
    Dim key As String
    Dim outputRow As Long
    Dim item As Variant
    Dim parts As Variant

    Set wsSource = ThisWorkbook.Worksheets("元データ")
    Set wsOutput = ThisWorkbook.Worksheets("集計結果")
    Set dict = CreateObject("Scripting.Dictionary")

    'A列を基準に最終行を取得
    lastRow = wsSource.Cells(wsSource.Rows.Count, "A") _
                      .End(xlUp).Row

    With wsSource

        For i = 2 To lastRow

            'A列とC列が両方空白の行は集計しない
            If Len(.Cells(i, "A").Value) > 0 Or _
               Len(.Cells(i, "C").Value) > 0 Then

                'F列が数値の行だけを集計
                If IsNumeric(.Cells(i, "F").Value) Then

                    key = CStr(.Cells(i, "A").Value) & vbTab & _
                          CStr(.Cells(i, "C").Value)

                    If dict.Exists(key) Then
                        dict(key) = dict(key) + _
                                    CDbl(.Cells(i, "F").Value)
                    Else
                        dict.Add key, _
                                 CDbl(.Cells(i, "F").Value)
                    End If

                End If
            End If

        Next i

    End With

    '以前の集計結果を消す(集計専用シートを想定)
    wsOutput.Range("A2:C" & wsOutput.Rows.Count) _
            .ClearContents

    'Dictionaryの内容をA列・B列・C列へ書き出す
    outputRow = 2

    For Each item In dict.Keys

        parts = Split(CStr(item), vbTab)

        wsOutput.Cells(outputRow, "A").Value = parts(0)
        wsOutput.Cells(outputRow, "B").Value = parts(1)
        wsOutput.Cells(outputRow, "C").Value = dict(item)

        outputRow = outputRow + 1

    Next item

End Sub
i

変数は多く見えますが、それぞれ「元シート」「出力先」「最終行」「キー」「出力行」など役割が異なります。名前を役割に合わせておくと、処理の流れを追いやすくなります。

CHECK POINTS

使う前に確認しておきたい点

  • 最終行の基準には、途中で空白にならない列を選ぶ
  • F列に文字が混ざる可能性がある場合は、IsNumericで確認する
  • vbTabを含むデータを扱う場合は、別の区切り方法を検討する
  • 英字の大文字・小文字を同じものとして扱うなら、追加前にdict.CompareMode = vbTextCompareを設定する
  • Dictionaryから出力される順序に頼らず、必要なら出力後に並べ替える
出力先の消去範囲

サンプルでは集計専用シートを前提にA~C列を消去します。ほかのデータがある場合は、必要な列だけを消すように変更してください。

SUMMARY

Dictionaryは、順不同の重複集計をシンプルにする

最初にコードを見ると少しハードルが高く感じますが、中心になる考え方は「同じキーがあれば足す、なければ新しく登録する」だけです。

  • For~Nextで元データを一行ずつ確認する
  • A列とC列を連結し、複数項目を一つのキーとして扱う
  • 同じキーのF列をDictionary内で加算する
  • 最後に重複のない結果を別シートへ書き出す

実際に一度動かしてみると、複数条件の集計でも構造はそれほど複雑ではありません。重複データをまとめる処理が必要になったとき、ぜひ候補にしたい方法です。

VBA・Excel自動化の記事