0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【VBA】Dictionary+配列の更新処理を関数化してみた

0
Last updated at Posted at 2025-12-16

目次

  1. はじめに
  2. 辞書に配列を格納する場面
  3. 配列では自動作成が使えない
  4. Exists を使った正しい書き方
  5. 汎用的な関数として使う
  6. 実際に試してみる
  7. まとめ

はじめに

前回の記事では、辞書(Dictionary)で単純な数値をカウントする際にIf~Exists~Addが不要になる便利な仕様を紹介しました。しかし、辞書の値として配列を使う場合は、この便利な仕様が使えません。この記事では、配列を扱う際の正しい書き方を解説します。

辞書に配列を格納する場面

商品ごとに複数の情報(例えば、1月・2月・3月の売上)を管理したい場合、辞書の値として配列を使うことがあります。

' 商品Aの3ヶ月分の売上を配列で管理
dict("商品A") = Array(100, 200, 150)

このように、1つのキーに対して複数の値をまとめて持たせたい場合に配列が便利です。

配列では自動作成が使えない

単純な数値の場合は以下のように書けましたが、配列の場合は同じ方法が使えません。

' 数値の場合(これはOK)
dict("商品A") = dict("商品A") + 1

' 配列の場合(これはエラーになる)
dict("商品A")(0) = dict("商品A")(0) + 100

配列の要素に直接アクセスしようとすると、キーが存在しない場合にエラーが発生してしまいます。辞書の自動作成機能は、配列の要素レベルまでは面倒を見てくれないためです。

Exists を使った正しい書き方

配列を扱う場合は、Existsで存在確認をしてから処理する必要があります。

基本的な流れは以下の通りです。

  1. Existsでキーの存在を確認
  2. 存在しない場合は配列を新規作成してAdd
  3. 存在する場合は配列を取り出して値を更新し、書き戻す
If Not dict.Exists("商品A") Then
    ' 新規作成
    Dim newArr(1 To 3) As Long
    newArr(1) = 100: newArr(2) = 200: newArr(3) = 150
    dict.Add "商品A", newArr
Else
    ' 既存の配列を更新
    Dim tmp As Variant
    tmp = dict("商品A")
    tmp(1) = tmp(1) + 100
    dict("商品A") = tmp
End If

ここで重要なのは、辞書から配列を一度取り出して、変更してから書き戻すという手順です。dict("商品A")(1) = ...のように直接変更することはできません。

配列の場合は一時変数(tmp)を使って「取り出し→変更→書き戻し」という手順が必要になります。これは辞書の仕様上の制約です。

汎用的な関数として使う

配列の登録・更新処理を毎回書くのは大変なので、関数として汎用化しておくと便利です。以下のような関数を作成しておけば、どこからでも呼び出せます。

' 辞書に対して新規登録または更新を行う関数
' 引数: 辞書、キー、配列
Public Sub UpdateDict(ByRef dict As Scripting.Dictionary, _
    ByVal key As String, ByVal arr As Variant)

    ' キー存在確認
    If Not dict.Exists(key) Then
        ' 新規登録
        dict.Add key, arr
    Else
        ' 既存 -> 配列全体を加算
        Dim i As Long
        Dim tmp As Variant: tmp = dict(key)
        For i = LBound(arr) To UBound(arr)
            tmp(i) = tmp(i) + arr(i)
        Next i
        dict(key) = tmp
    End If
End Sub

この関数の特徴は、LBoundUBoundを使って配列の開始位置と終了位置を自動判定している点です。配列が(1 To 3)でも(0 To 2)でも自動的に対応できます。

【関数の使い方】

Sub TestUpdateDictFunction()
    Dim dict As New Scripting.Dictionary
    Dim sales(1 To 3) As Long
    
    ' 商品Aの売上データ
    sales(1) = 100: sales(2) = 200: sales(3) = 150
    UpdateDict dict, "商品A", sales
    
    ' 商品Aの追加売上
    sales(1) = 50: sales(2) = 80: sales(3) = 30
    UpdateDict dict, "商品A", sales
    
    ' 結果確認
    Dim result As Variant: result = dict("商品A")
    Debug.Print result(1), result(2), result(3)  ' 150, 280, 180
End Sub

関数化することで、コードの見通しが良くなり、同じ処理を繰り返し書く必要がなくなります。

LBoundは配列の最小インデックス、UBoundは配列の最大インデックスを返す関数です。これらを使うことで、配列のサイズに関係なく柔軟に対応できます。

実際に試してみる

実際に動作を確認できるコードを用意しました。空白のExcelブックを開き、以下の手順で試してみてください。

  1. 新しいExcelブックを開く
  2. Alt + F11でVBエディタを開く
  3. 「ツール」→「参照設定」から「Microsoft Scripting Runtime」にチェックを入れる
  4. 「挿入」→「標準モジュール」で新しいモジュールを追加
  5. 以下のコードを貼り付けて実行

辞書を使うには参照設定が必要です。設定方法は以下の記事で解説しています。

【基本例】配列の新規登録と更新

Sub TestDictionaryWithArray()
    ' 辞書を作成
    Dim dict As New Scripting.Dictionary
    Dim sales(1 To 3) As Long
    
    ' 商品Aの売上データ(1月、2月、3月)
    sales(1) = 100: sales(2) = 200: sales(3) = 150
    UpdateDict dict, "商品A", sales
    
    ' 商品Aの追加売上
    sales(1) = 50: sales(2) = 80: sales(3) = 30
    UpdateDict dict, "商品A", sales
    
    ' 結果を表示
    Dim result As Variant
    result = dict("商品A")
    
    Dim msg As String
    msg = "商品Aの売上集計結果" & vbCrLf & vbCrLf
    msg = msg & "1月: " & result(1) & "円" & vbCrLf
    msg = msg & "2月: " & result(2) & "円" & vbCrLf
    msg = msg & "3月: " & result(3) & "円"
    
    MsgBox msg, vbInformation
End Sub

' 辞書に対して新規登録または更新を行う関数
Public Sub UpdateDict(ByRef dict As Scripting.Dictionary, _
    ByVal key As String, ByVal arr As Variant)

    If Not dict.Exists(key) Then
        dict.Add key, arr
    Else
        Dim i As Long
        Dim tmp As Variant: tmp = dict(key)
        For i = LBound(arr) To UBound(arr)
            tmp(i) = tmp(i) + arr(i)
        Next i
        dict(key) = tmp
    End If
End Sub

実行すると、以下の結果が表示されます。

商品Aの売上集計結果

1月: 150円
2月: 280円
3月: 180円

【応用例】複数商品の売上を集計してシートに出力

より実用的な例として、複数の商品データを集計してシートに出力するコードです。

Sub AggregateProductSales()
    ' シートをクリア
    Cells.Clear
    
    ' 辞書を作成
    Dim dict As New Scripting.Dictionary
    Dim sales(1 To 3) As Long
    
    ' ====== データ1回目の登録 ======
    ' 商品Aのデータ
    sales(1) = 100: sales(2) = 200: sales(3) = 150
    UpdateDict dict, "商品A", sales
    
    ' 商品Bのデータ
    sales(1) = 80: sales(2) = 120: sales(3) = 90
    UpdateDict dict, "商品B", sales
    
    ' ====== データ2回目の登録(加算される) ======
    ' 商品Aの追加データ
    sales(1) = 50: sales(2) = 80: sales(3) = 30
    UpdateDict dict, "商品A", sales
    
    ' 商品Cのデータ(新規)
    sales(1) = 200: sales(2) = 150: sales(3) = 180
    UpdateDict dict, "商品C", sales
    
    ' 商品Bの追加データ
    sales(1) = 40: sales(2) = 60: sales(3) = 50
    UpdateDict dict, "商品B", sales
    
    ' ====== 結果をシートに出力 ======
    ' 見出し行
    Range("A1").Value = "商品名"
    Range("B1").Value = "1月"
    Range("C1").Value = "2月"
    Range("D1").Value = "3月"
    Range("E1").Value = "合計"
    
    ' データ行
    Dim key As Variant
    Dim row As Long: row = 2
    
    For Each key In dict.Keys
        Dim arr As Variant
        arr = dict(key)
        
        Cells(row, 1).Value = key
        Cells(row, 2).Value = arr(1)
        Cells(row, 3).Value = arr(2)
        Cells(row, 4).Value = arr(3)
        Cells(row, 5).Formula = "=SUM(B" & row & ":D" & row & ")"
        
        row = row + 1
    Next key
    
    ' 列幅を自動調整
    Columns("A:E").AutoFit
    
    MsgBox "売上集計が完了しました", vbInformation
End Sub

' 辞書に対して新規登録または更新を行う関数
Public Sub UpdateDict(ByRef dict As Scripting.Dictionary, _
    ByVal key As String, ByVal arr As Variant)

    If Not dict.Exists(key) Then
        dict.Add key, arr
    Else
        Dim i As Long
        Dim tmp As Variant: tmp = dict(key)
        For i = LBound(arr) To UBound(arr)
            tmp(i) = tmp(i) + arr(i)
        Next i
        dict(key) = tmp
    End If
End Sub

このコードを実行すると、自動的に集計が行われ、以下のような表がシートに出力されます。

商品名 1月 2月 3月 合計
商品A 150 280 180 610
商品B 120 180 140 440
商品C 200 150 180 530

UpdateDict関数を使うことで、配列の登録・更新処理を簡潔に記述できています。同じ関数を何度も呼び出すだけで、自動的に新規登録や加算が行われます。

まとめ

辞書の値として配列を使う場合は、単純な数値のときとは異なり、Existsを使った明示的な存在確認が必要です。配列を更新する際は「取り出し→変更→書き戻し」という手順を踏む必要があります。この処理をUpdateDictのような関数にまとめておくと、繰り返し使う場合に便利で、コードの可読性(読みやすさ)も向上します。

0
0
0

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?