In PHP kann man sehr übersichtlich mit assoziativen Arrays arbeiten, wie zum Beispiel
$MeinArray['Vorname'] = 'Manfred';
In Excel geht das auch:
Setzen Sie im VBA-Editor unter Extras ▸ Verweise ein Häkchen vor Microsoft Scripting Runtime oder verwenden Sie Late Binding wie in den folgenden Beispielen. Nun können Sie wie folgt mit assoziativen Arrays arbeiten:
Basis ist das Dictionary oder die Collection. Dabei spielen immer Paare eine Rolle: Key=Item, also ein Schlüsselname und der Eintrag dazu. In den folgenden Beispielen wird der Einfachheit halber (und weil Collection ein paar Nachteile hat) nur das Dictionary verwendet.
Im Code sind hier die einzelnen Inhalte (Keys, Items) zum besseren Verständnis hardcodiert, also fest eingetragen. An dieser Stelle können natürlich auch Variablen oder Zellen verwendet werden, zum Beispiel Spalten- oder Zeilenüberschriften und Zellen mit Daten.
Eine Ebene
Im Beispiel sind die Key=Item-Paare auf einer Ebene. Das heiÃ?t, es darf auch keine zwei gleichnamigen Keys geben, sonst wird eine Fehlermeldung ausgegeben.
Sub AssozArray_Einfach()
Dim dict, varKey, varKeys, varItems
Dim intI As Integer
Set dict = CreateObject("Scripting.Dictionary")
dict.Add "name", "Müller"
dict.Add "vorname", "Johann"
dict.Add "wohnort", "Berlin"
MsgBox dict("vorname") & " " & dict("name") & " wohnt in " & vbNewLine & dict("wohnort") & "."
'For Each varKey In dict.keys
' MsgBox varKey & vbNewLine & dict(varKey)
'Next
varKeys = dict.Keys
varItems = dict.Items
For intI = 0 To dict.Count - 1
MsgBox varKeys(intI) & ": " & vbNewLine & varItems(intI)
Next
Set dict = Nothing
End Sub
Die MsgBox zeigt, wie die Syntax Dictionary(Key) verwendet werden kann, um auf die einzelnen Elemente zuzugreifen. Darunter sind zwei Schleifen. Mit ihnen wird demonstriert, wie alle Elemente des Dictionarys ausgegeben werden können.
Verschachtelungen, mehrere Ebenen
Elemente von Arrays können wiederum Arrays sein. Dies geht auch bei den assoziativen Arrays bzw. Dictionarys in VBA - auch hier kann verschachtelt werden. Dabei trifft genau das Prinzip der Paare zu, nur dass an der Stelle des Items eine weitere Sammlung (Dictionary oder Collection) ist. Hier wird ein temporäres Dictionary verwendet, das vor jeder Verschachtelung neu erzeugt und danach wieder auf Nothing gesetzt wird. Vorbelegen oder �berschreiben der Items in den einzelnen Schritten funktioniert nicht, da sonst nur auf die letzten Einträge referenziert wird.
Wir wollen im Beispiel ein Dictionary aus sechs Personen erzeugen, das sind also sechs verschiedene Keys. Diesen sechs Personen können nun Details (Nachname, Vorname, Wohnort) zugeordnet werden, müssen aber nicht. So kann der Platz für eine Person auch nur reserviert sein, ohne dass Details enthalten sind.
Die Personen insgesamt sind die Einträge in dicPersonen mit den Keys Person x. Werden Details zugeordnet, erfolgt das Speichern zunächst im temporären Dictionary dictDetails, das dann als Item der Person zugeordnet wird. Gibt es keine Details, wird als Item einfach ein String verwendet.
Sub AssozArray_Levels()
Dim dicPersonen, dictDetails, varKey, varKey1
Dim strAusg As String
Set dicPersonen = CreateObject("Scripting.Dictionary")
Set dictDetails = CreateObject("Scripting.Dictionary")
dictDetails.Add "nachname", "Müller"
dictDetails.Add "vorname", "Klaus"
dictDetails.Add "wohnort", "Berlin"
dicPersonen.Add "Person 1", dictDetails
Set dictDetails = Nothing
Set dictDetails = CreateObject("Scripting.Dictionary")
dictDetails.Add "nachname", "Beispielfrau"
dictDetails.Add "vorname", "Bärbel"
dictDetails.Add "wohnort", "Hamburg"
dicPersonen.Add "Person 2", dictDetails
Set dictDetails = Nothing
Set dictDetails = CreateObject("Scripting.Dictionary")
dictDetails.Add "nachname", "Mustermann"
dictDetails.Add "vorname", "Franz"
dictDetails.Add "wohnort", "Leipzig"
dicPersonen.Add "Person 3", dictDetails
Set dictDetails = Nothing
dicPersonen.Add "Person 4", "Nicht vergeben."
Set dictDetails = CreateObject("Scripting.Dictionary")
dictDetails.Add "nachname", "Sonne"
dictDetails.Add "vorname", "Klara"
dictDetails.Add "wohnort", "München"
dicPersonen.Add "Person 5", dictDetails
Set dictDetails = Nothing
dicPersonen.Add "Person 6", "Auch nicht vergeben."
MsgBox "Testausgabe: " & vbNewLine & dicPersonen("Person 1")("wohnort") & vbNewLine & dicPersonen("Person 2")("wohnort")
strAusg = ""
For Each varKey In dicPersonen.keys
strAusg = strAusg & vbNewLine & "Person: " & varKey & vbNewLine
On Error Resume Next
For Each varKey1 In dicPersonen(varKey).keys
strAusg = strAusg & "key: " & varKey1 & vbTab & "item: " & dicPersonen(varKey)(varKey1) & vbNewLine
Next
If Err.Number = 92 Then
strAusg = strAusg & vbTab & vbTab & "item: " & dicPersonen(varKey) & vbNewLine
Err.Clear
End If
Next
MsgBox strAusg
Set dicPersonen = Nothing
End Sub
In der MsgBox mit der Testausgabe ist zu sehen, wie die Items ausgegeben werden können. Wir haben hier also nicht nur den Key einer Ebene, sondern hier wird mit Klammerpaaren bis zur unteren Ebene gearbeitet.
Schwieriger ist es mit der kompletten Ausgabe per Schleife. Hätten wir konsequent bei jeder Person eine Verschachtelung mit den Details, würde so etwas reichen:
For Each varKey In dicPersonen.keys
strAusg = strAusg & vbNewLine & "Person: " & varKey & vbNewLine
For Each varKey1 In dicPersonen(varKey).keys
strAusg = strAusg & "key: " & varKey1 & vbTab & "item: " & dicPersonen(varKey)(varKey1) & vbNewLine
Next
Next
Da aber zwischendurch unverschachtelte Items direkt auf der ersten Ebene sind (Person 4 + 6), käme es hier zu einer Fehlermeldung, dem Fehler 92. Der wird abgefangen und separat behandelt; hier wird der Eintrag direkt ausgegeben. Der Schlüssel, also der Key, ist hier ja die oberste Ebene, die Person selbst.
Dynamische Arrayformel
Zum Verdeutlichen wird hier die zweite Variante als Array ausgegeben, so dass sie als Formel in einer Zelle verwendet werden kann (funktioniert ab Excel 365).
In die Zelle kann einfach =AssozArray_Array() eingegeben werden; als Ergebnis müssten die Key-Item-Paare zu jeder Person erscheinen.
Zum praktischen Einsatz ist dies sicher eher weniger geeignet, da dann eine andere Datenstruktur erforderlich wäre. Sollte das gebraucht werden, lässt sich aber der Array, der erstellt wird, einfach anpassen.
Function AssozArray_Array()
Dim dicPersonen, dictDetails, varKey, varKey1
Dim strAusg As String
Dim arrRet(1 To 14, 1 To 3), lngRet As LongPtr, intS As Integer
Set dicPersonen = CreateObject("Scripting.Dictionary")
Set dictDetails = CreateObject("Scripting.Dictionary")
dictDetails.Add "nachname", "Müller"
dictDetails.Add "vorname", "Klaus"
dictDetails.Add "wohnort", "Berlin"
dicPersonen.Add "Person 1", dictDetails
Set dictDetails = Nothing
Set dictDetails = CreateObject("Scripting.Dictionary")
dictDetails.Add "nachname", "Beispielfrau"
dictDetails.Add "vorname", "Bärbel"
dictDetails.Add "wohnort", "Hamburg"
dicPersonen.Add "Person 2", dictDetails
Set dictDetails = Nothing
Set dictDetails = CreateObject("Scripting.Dictionary")
dictDetails.Add "nachname", "Mustermann"
dictDetails.Add "vorname", "Franz"
dictDetails.Add "wohnort", "Leipzig"
dicPersonen.Add "Person 3", dictDetails
Set dictDetails = Nothing
dicPersonen.Add "Person 4", "Nicht vergeben."
Set dictDetails = CreateObject("Scripting.Dictionary")
dictDetails.Add "nachname", "Sonne"
dictDetails.Add "vorname", "Klara"
dictDetails.Add "wohnort", "München"
dicPersonen.Add "Person 5", dictDetails
Set dictDetails = Nothing
dicPersonen.Add "Person 6", "Auch nicht vergeben."
lngRet = 0
For Each varKey In dicPersonen.keys
On Error Resume Next
For Each varKey1 In dicPersonen(varKey).keys
If varKey1 <> "" Then
lngRet = lngRet + 1
arrRet(lngRet, 1) = varKey
arrRet(lngRet, 2) = varKey1
arrRet(lngRet, 3) = dicPersonen(varKey)(varKey1)
End If
Next
If Err.Number = 92 Then
If dicPersonen(varKey) <> "" Then
lngRet = lngRet + 1
arrRet(lngRet, 1) = varKey
arrRet(lngRet, 2) = ""
arrRet(lngRet, 3) = dicPersonen(varKey)
End If
Err.Clear
End If
Next
Set dicPersonen = Nothing
AssozArray_Array = arrRet
End Function
Website-Tipp
Hier gibt es gute Erklärungen von Paul Kelly (englisch): Excel VBA Dictionary â?? A Complete Guide