Dictionaryオブジェクト
VBA で効率的にデータを管理する方法の一つとして、Dictionary オブジェクトがあります。Dictionary は、キーと値のペアでデータを保存・管理できる便利なデータ構造で、配列や Collection オブジェクトでは実現しにくい柔軟なデータ操作が可能になります。
この記事では、Dictionary オブジェクトの基本的な使い方から、実践的な活用例まで詳しく解説します。
Dictionary オブジェクトとは
Dictionary オブジェクトは、キー(Key)と値(Value)のペアでデータを保存するデータ構造です。他のプログラミング言語では「連想配列」「ハッシュテーブル」「マップ」などと呼ばれることもあります。
Dictionary の特徴
Dictionary オブジェクトには以下のような特徴があります:
- キーによる高速検索: 配列のようにインデックス番号ではなく、任意のキーで値を取得できます
- 一意性の保証: 同じキーは重複できないため、データの重複を防げます
- 柔軟なデータ型: キーと値に様々なデータ型を使用できます
- 動的なサイズ変更: 配列と違い、要素数を事前に宣言する必要がありません
Collection との違い
VBA には Collection という似たオブジェクトもありますが、以下の点で Dictionary が優れています:
機能 | Dictionary | Collection |
---|---|---|
キーの存在確認 | ○ 可能 | × 不可 |
キーによる値の更新 | ○ 可能 | × 不可 |
キーの取得 | ○ 可能 | × 不可 |
高速な検索 | ○ 高速 | △ 低速 |
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 を使う方法(推奨)
参照設定なしで使用できるため、他の環境でも動作する汎用性の高い方法です。
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 に新しいキーと値のペアを追加します。
dict.Add Key:="apple", Item:=100
dict.Add "banana", 150 ' 引数名は省略可能
' すでに存在するキーを追加しようとするとエラー
' dict.Add "apple", 200 ' エラー: キーが既に関連付けられています
同じキーでAdd
メソッドを呼び出すとエラーになります。キーが存在するか確認してから追加するか、後述する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 ' 新しく追加される
存在しないキーを指定して値を代入すると、自動的にそのキーと値が追加されます。この挙動を利用すれば、Exists
メソッドで確認せずに値を設定できます。
Exists メソッド - キーの存在確認
指定したキーが Dictionary に存在するか確認します。
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 メソッド - 要素の削除
指定したキーの要素を削除します。
dict.Remove "banana"
' 存在しないキーを削除しようとするとエラー
If dict.Exists("melon") Then
dict.Remove "melon"
End If
RemoveAll メソッド - 全要素の削除
Dictionary のすべての要素を削除します。
dict.RemoveAll
Debug.Print dict.Count ' => 0
Count プロパティ - 要素数の取得
Dictionary に格納されている要素の数を取得します。
Debug.Print "要素数: " & dict.Count
' ループ処理での活用
If dict.Count > 0 Then
Debug.Print "データが存在します"
End If
Keys メソッド - すべてのキーを取得
Dictionary に格納されているすべてのキーを配列として取得します。
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 に格納されているすべての値を配列として取得します。
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 プロパティ - 比較モードの設定
キーの比較方法を設定します。
' 大文字小文字を区別しない(デフォルト:区別する)
dict.CompareMode = vbTextCompare
dict.Add "Apple", 100
Debug.Print dict("apple") ' => 100 (大文字小文字を区別しない)
' バイナリモード(大文字小文字を区別)
dict.CompareMode = vbBinaryCompare
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 関数のように、キーをもとに別のシートからデータを取得します。
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やワークシート関数よりも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 を格納することで、より複雑なデータ構造を扱えます。
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
Dictionaryはオブジェクトなので、代入時には必ずSet
キーワードを使用してください。
まとめ
Dictionary オブジェクトは、VBA でデータを効率的に管理するための強力なツールです。主な利点をまとめると:
- 高速な検索・アクセス: キーを使った瞬時のデータ取得
- 重複の自動排除: 同じキーは登録できないため、データの一意性が保証される
- 柔軟なデータ管理: 様々なデータ型を扱え、動的にサイズ変更可能
- コードの可読性向上: インデックス番号ではなく、意味のあるキー名でアクセスできる
配列や Collection と比較して、Dictionary は学習コストに見合うだけの価値がある機能です。特に大量のデータを扱う場合や、キーと値のペアでデータを管理したい場合に威力を発揮します。
ぜひ実際のプロジェクトで Dictionary を活用して、より効率的で保守性の高いコードを書いてみてください。