[VBA][HowTo][Dictionary][multidimensional]多次元の連想配列の作り方

今回はExcelのリストをVBAのDictionaryに取り込む方法について考えてみたいと思います。

例えば、以下のような表があったとします。

VBAのDictionaryで取り込むにしてもどうやって取り込んだらいいのだろう?

Dictionaryオブジェクトは「Key」と「Item」で構成された連想配列なのだが、表のような項目をどうやってDictionaryへ取り込むことが出来るのだろう?

悩ましいですよね。

これを解決するにはちょっとした発想の転換が必要かもしれません。


多次元の連想配列で表を取り込む方法

Sub Samplle()
Dim oDic As Dictionary
Dim oDic_Child As Dictionary
Dim oCol As Collection
Dim arrList As Variant
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim buf As String
        
    arrList = Worksheets("Sheet1").Range("A1").CurrentRegion.Value
    
    Set oDic = New Dictionary
        
    For i = LBound(arrList, 1) + 1 To UBound(arrList, 1)
            
        Set oDic_Child = New Dictionary
        Set oCol = New Collection
        
        oDic_Child.Add arrList(1, 1), arrList(i, 1)
        oDic_Child.Add arrList(1, 2), arrList(i, 2)
        oDic_Child.Add arrList(1, 3), arrList(i, 3)
        oDic_Child.Add arrList(1, 4), arrList(i, 4)
        
        If Not IsEmpty(arrList(i, 5)) Then oCol.Add arrList(i, 5)
        If Not IsEmpty(arrList(i, 6)) Then oCol.Add arrList(i, 6)
        If Not IsEmpty(arrList(i, 7)) Then oCol.Add arrList(i, 7)
        
        oDic_Child.Add arrList(1, 5), oCol
        
        oDic.Add arrList(i, 1), oDic_Child
        
        Set oCol = Nothing
        Set oDic_Child = Nothing
        
    Next
    
    Erase arrList
    
    For i = 0 To oDic.Count - 1
        buf = vbNullString
        arrList = oDic(oDic.Keys(i)).Keys
        For j = LBound(arrList) To UBound(arrList)
            If VarType(oDic(oDic.Keys(i))(arrList(j))) <> vbObject Then
                buf = buf & """" & oDic(oDic.Keys(i))(arrList(j)) & ""","
            Else
                For k = 1 To oDic(oDic.Keys(i))(arrList(j)).Count
                    buf = buf & """" & oDic(oDic.Keys(i))(arrList(j))(k) & ""","
                Next
            End If
        Next
        buf = Mid(buf, 1, Len(buf) - 1)
        Debug.Print buf
    Next
    
    Set oDic = Nothing

End Sub

連想配列の多次元化のロジック解説

Dictionaryオブジェクトの中にDictionaryオブジェクトを格納するというやり方です。

まずDictionaryに設定できるキーは重複しないことが前提になります。

今回のサンプルで使用した表は、「user_id」という重複しない項目が存在するため、Dictionary「oDic」のキーとして「user_id」を活用していきます。

Dictionary「oDic」の値として、レコード毎にキーに対応するDictionary「oDic_Child」を作成していくのですが、ここで問題にぶつかります。

表の中の「favorite_subject」の項目の内訳の数がレコード毎で異なる点をどのように解決するかという部分です。

Dictionaryは重複しないキーと値がセットでなければ格納することが出来ません。

「favorite_subject」の中に複数の値を格納する必要があるわけです。

解決策としてCollectionオブジェクトを活用します。

CollectionオブジェクトはDictionaryオブジェクトと似た構造を持っているのですが、キーを必要とせずに値を追加していくことが出来ます。

「favorite_subject」の中をCollectionオブジェクトで管理することができれば、同項目名内の複数の値を一つにキーで設定することが可能になります。

そのような流れで作成されるDictionary「oDic_Child」をレコード毎でDictionary「oDic」の値として設定していくことで多次元の連想配列が出来上がります。

後半は作成したDictionaryをとりあえずイミディエイトウィンドウに出力してみました。

ネストが深いので美しくないですが、とりあえずのイミディエイトウィンドウ出力なので勘弁して下さい。

ポイントはVartype関数でCollectionとそれ以外を判定している点でしょうか。

連想配列の多次元化のまとめ

そもそもDictionaryを多次元化する必要性があるのか否かについては正直分かりません。

せいぜい2次元でしょう。

今回は方法論として挙げていますが、最終的に出来上がったDictionaryでいったい何をどうしたいのかによっては、もっと簡単な解決方法があるかもしれません。

Dictionaryの階層が深くなれば深くなるほど可読性が低下するため、メンテナンスが発生した際にはおそらく苦労します。

私、個人としては別の方法を合わせて検討すると思います。

構造体の配列やクラスの配列、コレクションなども代用出来ますし、場合によってはDB処理もいいかもしれません。

Dictionaryオブジェクトは大変便利なオブジェクトですが、行き過ぎるとせっかくのメリットが損なわれます。

可読性や簡素化の観点から適度に活用していくのがベストではないでしょうか。