Dictionaryオブジェクト

にメンテナンス済み

VBA で効率的にデータを管理する方法の一つとして、Dictionary オブジェクトがあります。Dictionary は、キーと値のペアでデータを保存・管理できる便利なデータ構造で、配列や Collection オブジェクトでは実現しにくい柔軟なデータ操作が可能になります。

この記事では、Dictionary オブジェクトの基本的な使い方から、実践的な活用例まで詳しく解説します。

Dictionary オブジェクトとは

Dictionary オブジェクトは、キー(Key)と値(Value)のペアでデータを保存するデータ構造です。他のプログラミング言語では「連想配列」「ハッシュテーブル」「マップ」などと呼ばれることもあります。

Dictionary の特徴

Dictionary オブジェクトには以下のような特徴があります:

  • キーによる高速検索: 配列のようにインデックス番号ではなく、任意のキーで値を取得できます
  • 一意性の保証: 同じキーは重複できないため、データの重複を防げます
  • 柔軟なデータ型: キーと値に様々なデータ型を使用できます
  • 動的なサイズ変更: 配列と違い、要素数を事前に宣言する必要がありません

Collection との違い

VBA には Collection という似たオブジェクトもありますが、以下の点で Dictionary が優れています:

機能DictionaryCollection
キーの存在確認○ 可能× 不可
キーによる値の更新○ 可能× 不可
キーの取得○ 可能× 不可
高速な検索○ 高速△ 低速
参照設定について

Dictionary オブジェクトを使用するには、「Microsoft Scripting Runtime」への参照設定が必要です。ただし、CreateObject("Scripting.Dictionary")を使用すれば参照設定なしでも利用できます。

Dictionary の基本的な使い方

参照設定を使う方法

VBA エディタで「ツール」→「参照設定」から「Microsoft Scripting Runtime」にチェックを入れます。

参照設定を使う方法
Dim dict As Scripting.Dictionary
Set dict = New Scripting.Dictionary

' 値の追加
dict.Add "apple", 100
dict.Add "banana", 150
dict.Add "orange", 120

' 値の取得
Debug.Print dict("apple")  ' => 100

CreateObject を使う方法(推奨)

参照設定なしで使用できるため、他の環境でも動作する汎用性の高い方法です。

CreateObjectを使う方法
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")

' 値の追加
dict.Add "apple", 100
dict.Add "banana", 150
dict.Add "orange", 120

' 値の取得
Debug.Print dict("apple")  ' => 100
推奨される方法

CreateObjectを使う方法は、参照設定が不要で他の環境でも動作するため、特別な理由がない限りこちらを使用することをお勧めします。

主要なプロパティとメソッド

Add メソッド - 要素の追加

Dictionary に新しいキーと値のペアを追加します。

Addメソッド
dict.Add Key:="apple", Item:=100
dict.Add "banana", 150  ' 引数名は省略可能

' すでに存在するキーを追加しようとするとエラー
' dict.Add "apple", 200  ' エラー: キーが既に関連付けられています
キーの重複に注意

同じキーでAddメソッドを呼び出すとエラーになります。キーが存在するか確認してから追加するか、後述するItemプロパティを使用して上書きしてください。

Item プロパティ - 値の取得・設定

指定したキーの値を取得または設定します。

Itemプロパティ
' 値の取得
Dim price As Long
price = dict.Item("apple")  ' => 100
price = dict("apple")       ' Itemは省略可能

' 値の設定(上書き)
dict.Item("apple") = 200
dict("apple") = 200  ' Itemは省略可能

' 存在しないキーを指定すると自動的に追加される
dict("grape") = 180  ' 新しく追加される
Itemプロパティの特性

存在しないキーを指定して値を代入すると、自動的にそのキーと値が追加されます。この挙動を利用すれば、Existsメソッドで確認せずに値を設定できます。

Exists メソッド - キーの存在確認

指定したキーが Dictionary に存在するか確認します。

Existsメソッド
If dict.Exists("apple") Then
    Debug.Print "りんごは存在します"
Else
    Debug.Print "りんごは存在しません"
End If

' 条件分岐で活用
If Not dict.Exists("melon") Then
    dict.Add "melon", 300
End If

Remove メソッド - 要素の削除

指定したキーの要素を削除します。

Removeメソッド
dict.Remove "banana"

' 存在しないキーを削除しようとするとエラー
If dict.Exists("melon") Then
    dict.Remove "melon"
End If

RemoveAll メソッド - 全要素の削除

Dictionary のすべての要素を削除します。

RemoveAllメソッド
dict.RemoveAll

Debug.Print dict.Count  ' => 0

Count プロパティ - 要素数の取得

Dictionary に格納されている要素の数を取得します。

Countプロパティ
Debug.Print "要素数: " & dict.Count

' ループ処理での活用
If dict.Count > 0 Then
    Debug.Print "データが存在します"
End If

Keys メソッド - すべてのキーを取得

Dictionary に格納されているすべてのキーを配列として取得します。

Keysメソッド
Dim keys As Variant
keys = dict.Keys

Dim i As Long
For i = LBound(keys) To UBound(keys)
    Debug.Print keys(i)
Next i

Items メソッド - すべての値を取得

Dictionary に格納されているすべての値を配列として取得します。

Itemsメソッド
Dim items As Variant
items = dict.Items

Dim i As Long
For i = LBound(items) To UBound(items)
    Debug.Print items(i)
Next i

CompareMode プロパティ - 比較モードの設定

キーの比較方法を設定します。

CompareModeプロパティ
' 大文字小文字を区別しない(デフォルト:区別する)
dict.CompareMode = vbTextCompare

dict.Add "Apple", 100
Debug.Print dict("apple")  ' => 100 (大文字小文字を区別しない)

' バイナリモード(大文字小文字を区別)
dict.CompareMode = vbBinaryCompare
CompareModeの設定タイミング

CompareModeプロパティは、Dictionaryが空の状態でのみ設定可能です。要素を追加した後に変更しようとするとエラーになります。

実践的な活用例

例 1: データの重複を除去する

配列やセル範囲から重複を除去してユニークな値を取得します。

重複除去
Sub RemoveDuplicates()
    Dim dict As Object
    Set dict = CreateObject("Scripting.Dictionary")

    ' サンプルデータ
    Dim arr As Variant
    arr = Array("apple", "banana", "apple", "orange", "banana", "grape")

    ' Dictionaryに追加(重複は自動的に無視される)
    Dim i As Long
    For i = LBound(arr) To UBound(arr)
        If Not dict.Exists(arr(i)) Then
            dict.Add arr(i), arr(i)
        End If
    Next i

    ' 結果を取得
    Dim uniqueValues As Variant
    uniqueValues = dict.Keys

    ' 結果を出力
    For i = LBound(uniqueValues) To UBound(uniqueValues)
        Debug.Print uniqueValues(i)
    Next i
    ' => apple, banana, orange, grape
End Sub

例 2: データの出現回数をカウントする

配列やセル範囲内の各要素が何回出現するかカウントします。

出現回数のカウント
Sub CountOccurrences()
    Dim dict As Object
    Set dict = CreateObject("Scripting.Dictionary")

    ' サンプルデータ
    Dim arr As Variant
    arr = Array("apple", "banana", "apple", "orange", "banana", "apple")

    ' カウント処理
    Dim i As Long
    For i = LBound(arr) To UBound(arr)
        If dict.Exists(arr(i)) Then
            dict(arr(i)) = dict(arr(i)) + 1
        Else
            dict.Add arr(i), 1
        End If
    Next i

    ' 結果を出力
    Dim key As Variant
    For Each key In dict.Keys
        Debug.Print key & ": " & dict(key) & "回"
    Next key
    ' => apple: 3回, banana: 2回, orange: 1回
End Sub

例 3: 2 つのシートのデータを紐付ける(VLOOKUP 代替)

VLOOKUP 関数のように、キーをもとに別のシートからデータを取得します。

VLOOKUPの代替
Sub LookupData()
    Dim dict As Object
    Set dict = CreateObject("Scripting.Dictionary")

    ' マスタシートからデータを読み込み
    Dim masterSheet As Worksheet
    Set masterSheet = ThisWorkbook.Sheets("マスタ")

    Dim lastRow As Long
    lastRow = masterSheet.Cells(masterSheet.Rows.Count, 1).End(xlUp).Row

    Dim i As Long
    For i = 2 To lastRow
        Dim productCode As String
        Dim productName As String

        productCode = masterSheet.Cells(i, 1).Value  ' A列:商品コード
        productName = masterSheet.Cells(i, 2).Value  ' B列:商品名

        dict.Add productCode, productName
    Next i

    ' データシートで検索
    Dim dataSheet As Worksheet
    Set dataSheet = ThisWorkbook.Sheets("データ")

    lastRow = dataSheet.Cells(dataSheet.Rows.Count, 1).End(xlUp).Row

    For i = 2 To lastRow
        Dim code As String
        code = dataSheet.Cells(i, 1).Value  ' A列:商品コード

        If dict.Exists(code) Then
            dataSheet.Cells(i, 2).Value = dict(code)  ' B列に商品名を設定
        Else
            dataSheet.Cells(i, 2).Value = "該当なし"
        End If
    Next i
End Sub
VLOOKUPより高速

大量のデータを扱う場合、VLOOKUPやワークシート関数よりもDictionaryを使った方が圧倒的に高速です。数万行のデータでも瞬時に処理できます。

例 4: グループ化とデータ集計

データをグループ化して集計処理を行います。

グループ化と集計
Sub GroupAndSum()
    Dim dict As Object
    Set dict = CreateObject("Scripting.Dictionary")

    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("売上データ")

    Dim lastRow As Long
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row

    ' データをグループ化して合計
    Dim i As Long
    For i = 2 To lastRow
        Dim category As String
        Dim amount As Long

        category = ws.Cells(i, 1).Value  ' A列:カテゴリ
        amount = ws.Cells(i, 2).Value    ' B列:金額

        If dict.Exists(category) Then
            dict(category) = dict(category) + amount
        Else
            dict.Add category, amount
        End If
    Next i

    ' 結果を出力
    Dim resultSheet As Worksheet
    Set resultSheet = ThisWorkbook.Sheets("集計結果")

    Dim row As Long
    row = 2

    Dim key As Variant
    For Each key In dict.Keys
        resultSheet.Cells(row, 1).Value = key
        resultSheet.Cells(row, 2).Value = dict(key)
        row = row + 1
    Next key
End Sub

例 5: ネストした Dictionary(複雑なデータ構造)

Dictionary の値として別の Dictionary を格納することで、より複雑なデータ構造を扱えます。

ネストしたDictionary
Sub NestedDictionary()
    Dim customers As Object
    Set customers = CreateObject("Scripting.Dictionary")

    ' 顧客1のデータ
    Dim customer1 As Object
    Set customer1 = CreateObject("Scripting.Dictionary")
    customer1.Add "名前", "山田太郎"
    customer1.Add "年齢", 30
    customer1.Add "住所", "東京都"

    ' 顧客2のデータ
    Dim customer2 As Object
    Set customer2 = CreateObject("Scripting.Dictionary")
    customer2.Add "名前", "佐藤花子"
    customer2.Add "年齢", 25
    customer2.Add "住所", "大阪府"

    ' 顧客データを追加
    customers.Add "C001", customer1
    customers.Add "C002", customer2

    ' データの取得
    Debug.Print customers("C001")("名前")  ' => 山田太郎
    Debug.Print customers("C002")("年齢")  ' => 25

    ' すべての顧客データを出力
    Dim customerId As Variant
    For Each customerId In customers.Keys
        Debug.Print "顧客ID: " & customerId
        Debug.Print "  名前: " & customers(customerId)("名前")
        Debug.Print "  年齢: " & customers(customerId)("年齢")
        Debug.Print "  住所: " & customers(customerId)("住所")
    Next customerId
End Sub

パフォーマンスの考慮事項

Dictionary が高速な理由

Dictionary はハッシュテーブルという仕組みを使っており、キーから値を取得する処理が非常に高速です。

パフォーマンス比較
' 配列で検索(遅い)
Dim arr As Variant
arr = Array("apple", "banana", "orange")

Dim i As Long
For i = LBound(arr) To UBound(arr)
    If arr(i) = "orange" Then
        ' 見つかった
    End If
Next i

' Dictionaryで検索(速い)
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
dict.Add "apple", 1
dict.Add "banana", 2
dict.Add "orange", 3

If dict.Exists("orange") Then
    ' 瞬時に見つかる
End If

大量データ処理のベストプラクティス

大量データ処理
Sub ProcessLargeData()
    Dim dict As Object
    Set dict = CreateObject("Scripting.Dictionary")

    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("データ")

    ' データを一括で配列に読み込み
    Dim dataRange As Range
    Set dataRange = ws.Range("A2:B10000")

    Dim data As Variant
    data = dataRange.Value  ' 配列に一括読み込み

    ' Dictionaryに格納
    Dim i As Long
    For i = 1 To UBound(data, 1)
        dict.Add data(i, 1), data(i, 2)
    Next i

    ' 処理...
End Sub
大量データ処理のコツ

セル範囲を直接ループするのではなく、一度配列に読み込んでから処理する方が高速です。1万件以上のデータを扱う場合は、この方法を使用してください。

よくある質問とトラブルシューティング

Q1: キーが存在しない場合のエラーを回避したい

エラー回避
' 悪い例:エラーになる可能性がある
Dim value As Variant
value = dict("存在しないキー")  ' エラー

' 良い例:Existsで確認
If dict.Exists("キー") Then
    value = dict("キー")
Else
    value = ""  ' デフォルト値
End If

Q2: Dictionary をループする方法

ループ処理
' 方法1: For Eachでキーをループ
Dim key As Variant
For Each key In dict.Keys
    Debug.Print key & ": " & dict(key)
Next key

' 方法2: 配列に変換してループ
Dim keys As Variant
Dim values As Variant
keys = dict.Keys
values = dict.Items

Dim i As Long
For i = LBound(keys) To UBound(keys)
    Debug.Print keys(i) & ": " & values(i)
Next i

Q3: Dictionary を関数の戻り値にしたい

関数の戻り値
Function CreateProductDictionary() As Object
    Dim dict As Object
    Set dict = CreateObject("Scripting.Dictionary")

    dict.Add "P001", "商品A"
    dict.Add "P002", "商品B"
    dict.Add "P003", "商品C"

    Set CreateProductDictionary = dict
End Function

' 使用例
Sub TestFunction()
    Dim products As Object
    Set products = CreateProductDictionary()

    Debug.Print products("P001")  ' => 商品A
End Sub
Setキーワードを忘れずに

Dictionaryはオブジェクトなので、代入時には必ずSetキーワードを使用してください。

まとめ

Dictionary オブジェクトは、VBA でデータを効率的に管理するための強力なツールです。主な利点をまとめると:

  1. 高速な検索・アクセス: キーを使った瞬時のデータ取得
  2. 重複の自動排除: 同じキーは登録できないため、データの一意性が保証される
  3. 柔軟なデータ管理: 様々なデータ型を扱え、動的にサイズ変更可能
  4. コードの可読性向上: インデックス番号ではなく、意味のあるキー名でアクセスできる

配列や Collection と比較して、Dictionary は学習コストに見合うだけの価値がある機能です。特に大量のデータを扱う場合や、キーと値のペアでデータを管理したい場合に威力を発揮します。

ぜひ実際のプロジェクトで Dictionary を活用して、より効率的で保守性の高いコードを書いてみてください。

#VBA #Dictionary #データ構造 #効率化