[VBA]Dictionay(連想配列)でのループ処理

VBA Dictionary ループVBA

VBAでDictionary(連想配列)を使用していると、Dictionary(連想配列)に格納したキーやデータの中身をループしながら処理を回していきたいロジックイメージって頻度が高いと思います。

せっかくDictionary(連想配列)にデータを格納したのだから何らかのループロジックで処理したいってのはプログラマにしてみれば自然な感覚といえます。

なぜならループとはプログラムの三大制御構造(順接、分岐、反復)のひとつだから。

今回はDictionary(連想配列)を使ったループ処理のやり方をテーマに取り上げてみようと思いますので、ループのやり方についてお悩みの方はぜひ参考にして頂ければ幸いです。

スポンサーリンク

Dictionay(連想配列)のループについて

Dictionay(連想配列)のループについて考えるとき、以下について決定する必要があります。

  • Dictionay(連想配列)を使ってループしたいのか
  • Dictionay(連想配列)を配列変数でループしたいのか

どちらのループも前後の処理の流れや宣言している変数の具合、単純な手法の好みなどで判断が変わってくるかもしれませんが、合わせて考えてみたいと思います。

Dictionay(連想配列)を使ったループ

Dictionary(連想配列)をダイレクトにループしたいときのループ方法になります。

For Each ~ Next を使ったループ

Sub Sample()
 
    Dim objDic As New Dictionary
    Dim varKey As Variant
   
    'Dictionary(連想配列)にデータを格納
    objDic.Add "国語", "Japanese"
    objDic.Add "数学", "Mathematics"
    objDic.Add "英語", "English"
    objDic.Add "理科", "Science"
    objDic.Add "地理", "Geography"
    objDic.Add "歴史", "History"
    
    'Dictionary(連想配列)のループ
    For Each varKey In objDic.Keys
        'キーとデータをデバッグ出力
        Debug.Print varKey & ":" & objDic.Item(varKey)
    Next
  
    'Dictionary(連想配列)を開放
    Set objDic = Nothing
    
End Sub

このロジックがたぶん一番簡潔なループだと思います。

VBAの For Each ~ Next ステートメントはグループにコレクションや配列のインスタンスを設定することができます。

Dictionaryオブジェクトの Keys メソッドを使うとキーの一次元配列のインスタンスをグループに設定できるので、 For Each ~ Next ステートメントでループ処理することが出来ます。

For ~ Next を使ったループ

Sub Sample()
 
    Dim objDic As New Dictionary
    Dim i As Integer
   
    'Dictionary(連想配列)にデータを格納
    objDic.Add "国語", "Japanese"
    objDic.Add "数学", "Mathematics"
    objDic.Add "英語", "English"
    objDic.Add "理科", "Science"
    objDic.Add "地理", "Geography"
    objDic.Add "歴史", "History"
    
    For i = LBound(objDic.Keys) To UBound(objDic.Keys)
        'キーとデータをデバッグ出力
        Debug.Print objDic.Keys(i) & ":" & objDic.Item(objDic.Keys(i))
    Next
  
    'Dictionary(連想配列)を開放
    Set objDic = Nothing
    
End Sub

For ~ Next ステートメントでループする方法も一般的ですよね。

ただしサンプルロジックを見てみると、Dictionaryオブジェクトの Keys メソッドを数回呼び出しているので、その点をどのように判断するかがひとつのポイントだと思います。

不必要に重複する情報の呼び出しはなるべく避けたいところですが、この点は好みで別れます。

レイトバインディングの場合は要注意

Dictionary(連想配列)で For ~ Next ステートメントを使用する場合の注意事項があります。

意外とインパクトのある話で、Dictionaryオブジェクトのバインディングの違いで作成したコードの挙動が異なってしまうという内容です。

VBScriptなどもレイトバインディングになるから似たようなエラーが発生するでしょう。

上のサンプルはアーリーバインディングで記述していますが、これをレイトバインディング仕様に変更すると以下のようなエラーになります。
For Next エラー レイトバインディング
このエラーは For Each ~ Next ステートメントでは発生しません。

For ~ Next を使ったループ改

Sub Sample()
 
    Dim objDic As New Dictionary
    Dim i As Integer

    'Dictionary(連想配列)にデータを格納
    objDic.Add "国語", "Japanese"
    objDic.Add "数学", "Mathematics"
    objDic.Add "英語", "English"
    objDic.Add "理科", "Science"
    objDic.Add "地理", "Geography"
    objDic.Add "歴史", "History"
    
    For i = LBound(objDic.Keys()) To UBound(objDic.Keys())
        'キーとデータをデバッグ出力
        Debug.Print objDic.Keys()(i) & ":" & objDic.Item(objDic.Keys()(i))
    Next
  
    'Dictionary(連想配列)を開放
    Set objDic = Nothing
    
End Sub

For ~ Next ステートメントのエラー対応版のループ処理の記述です。

みなさん、どこが変わったかお気づきでしょうか?

「objDic.Keys」の後ろに「()」が追加された以外は変更ございません。もちろんアーリーバインディングでも動きます。

For ~ Next ステートメントでDictionary(連想配列)をループする場合には「objDic.Keys」の後ろに「()」を付けることでバインディングがどちらであろうと問題なく動作します。

ちなみにVBScriptも同様の変更で動作するでしょう。(※変数宣言の箇所は要変更ですが)

Dictionary(連想配列)を配列変数でループ

Dictionary(連想配列)をダイレクトにループに使用するわけではなく、配列変数に割り当てた上で配列をループ処理に活用する方法です。

ロジックの前後の処理の流れ次第では配列変数にしておいたあげた方が、後々の処理を有利に進めることが出来るケースもあるため、ループ処理の手段にはあまり優劣はありません。

For Each ~ Next を使ったループ

Sub Sample()
 
    Dim objDic As New Dictionary
    Dim arrList As Variant
    Dim varKey As Variant

    'Dictionary(連想配列)にデータを格納
    objDic.Add "国語", "Japanese"
    objDic.Add "数学", "Mathematics"
    objDic.Add "英語", "English"
    objDic.Add "理科", "Science"
    objDic.Add "地理", "Geography"
    objDic.Add "歴史", "History"
    
    'Dictionary(連想配列)のキーを一次元配列化
    arrList = objDic.Keys
    
    For Each varKey In arrList
        'キーとデータをデバッグ出力
        Debug.Print varKey & ":" & objDic.Item(varKey)
    Next
  
    'Dictionary(連想配列)を開放
    Set objDic = Nothing
    
End Sub

For Each ~ Next ステートメントとして実行した処理は同じなので特筆する事項はありませんが、配列変数にしたことで他の用途があれば再利用できる利点がありますので、前後の処理次第で有利な方をご判断ください。

For ~ Next を使ったループ

Sub Sample()
 
    Dim objDic As New Dictionary
    Dim arrList As Variant
    Dim i As Integer

    'Dictionary(連想配列)にデータを格納
    objDic.Add "国語", "Japanese"
    objDic.Add "数学", "Mathematics"
    objDic.Add "英語", "English"
    objDic.Add "理科", "Science"
    objDic.Add "地理", "Geography"
    objDic.Add "歴史", "History"
    
    'Dictionary(連想配列)のキーを一次元配列化
    arrList = objDic.Keys
    
    For i = LBound(arrList) To UBound(arrList)
        'キーとデータをデバッグ出力
        Debug.Print arrList(i) & ":" & objDic.Item(arrList(i))
    Next
  
    'Dictionary(連想配列)を開放
    Set objDic = Nothing
    
End Sub

For ~ Next ステートメントを使用するなら、こちらのサンプルの方がすっきりした印象があるので可読性的には望ましいのかもしれません。

ちなみに For ~ Next ステートメントですが、「objDic.Keys」の後ろに「()」がなくてもバインディングに問題なくループ処理は動きます。

VBA、奥が深いです。

Dictionary(連想配列)のループのまとめ

いかがだったでしょうか?

たかがループですが、文字にしてみると意外とけっこうな文字数になってしまった気がします。

Do ~ Loop ステートメントだったり、まだ反復制御を実行できるループロジックはあるのですが、ここでは省いています。

ご興味のある方は記事の内容を参考にイメージを膨らませて頂ければ到達できると思います。

Dictionary(連想配列)のループについてお悩みの方のお役に立てたら幸いです。

タイトルとURLをコピーしました