Hat VBA eine Wörterbuchstruktur?

Antworten:

341

Ja.

Legen Sie einen Verweis auf die MS Scripting-Laufzeit fest ('Microsoft Scripting Runtime'). Gehen Sie gemäß dem Kommentar von @ regjo zu Extras-> Referenzen und aktivieren Sie das Kontrollkästchen für "Microsoft Scripting Runtime".

Referenzfenster

Erstellen Sie eine Wörterbuchinstanz mit dem folgenden Code:

Set dict = CreateObject("Scripting.Dictionary")

oder

Dim dict As New Scripting.Dictionary 

Anwendungsbeispiel:

If Not dict.Exists(key) Then 
    dict.Add key, value
End If 

Vergessen Sie nicht, das Wörterbuch auf einzustellen, Nothingwenn Sie es nicht mehr verwenden.

Set dict = Nothing 
Mitch Wheat
quelle
17
Dieser Datenstrukturtyp wird von der Skriptlaufzeit bereitgestellt, nicht von VBA. Grundsätzlich kann VBA praktisch jeden Datenstrukturtyp verwenden, auf den über eine COM-Schnittstelle zugegriffen werden kann.
David-W-Fenton
163
Nur der Vollständigkeit halber: Sie müssen auf die "Microsoft Scripting Runtime" verweisen, damit dies funktioniert (gehen Sie zu Tools-> Referenzen) und aktivieren Sie das entsprechende Kontrollkästchen.
Regjo
7
Äh, VBA-Sammlungen sind verschlüsselt. Aber vielleicht haben wir eine andere Definition von keyed.
David-W-Fenton
8
Ich verwende Excel 2010 ... aber ohne den Verweis auf "Microsoft Scripting Runtime" Tools - Ref .. Nur CreateObject zu tun, funktioniert NICHT. Also, @masterjo, ich denke dein Kommentar oben ist falsch. Es sei denn, mir fehlt etwas. Also, Leute Tools -> Referenzen sind erforderlich.
ihightower
4
Als FYI können Sie das nicht Dim dict As New Scripting.Dictionaryohne die Referenz verwenden. Ohne die Referenz müssen Sie die CreateObjectMethode der späten Bindung zum Instanziieren dieses Objekts verwenden.
David Zemens
179

VBA hat das Sammlungsobjekt:

    Dim c As Collection
    Set c = New Collection
    c.Add "Data1", "Key1"
    c.Add "Data2", "Key2"
    c.Add "Data3", "Key3"
    'Insert data via key into cell A1
    Range("A1").Value = c.Item("Key2")

Das CollectionObjekt führt schlüsselbasierte Suchvorgänge mit einem Hash durch, damit es schnell geht.


Mit einer Contains()Funktion können Sie überprüfen, ob eine bestimmte Sammlung einen Schlüssel enthält:

Public Function Contains(col As Collection, key As Variant) As Boolean
    On Error Resume Next
    col(key) ' Just try it. If it fails, Err.Number will be nonzero.
    Contains = (Err.Number = 0)
    Err.Clear
End Function

Bearbeiten 24. Juni 2015 : Kürzere Contains()dank @TWiStErRob.

Bearbeiten 25. September 2015 : Err.Clear()Dank @scipilot hinzugefügt.

Caleb Hattingh
quelle
5
Gut gemacht, um auf das eingebaute Collection-Objekt hinzuweisen, kann es als Wörterbuch verwendet werden, da die Add-Methode ein optionales "Schlüssel" -Argument hat.
Simon Tewsi
8
Das Schlechte am Auflistungsobjekt ist, dass Sie nicht überprüfen können, ob sich bereits ein Schlüssel in der Auflistung befindet. Es wird nur ein Fehler geworfen. Das ist die große Sache, die ich an Sammlungen nicht mag. (Ich weiß, dass es
Problemumgehungen
5
Beachten Sie, dass die Suche nach Zeichenfolgenschlüsseln (z. B. c.Item ("Key2")) im VBA-Wörterbuch IS-Hash ist, die Suche nach ganzzahligem Index (z. B. c.Item (20)) jedoch nicht - es ist eine lineare für / next Stilsuche und sollte vermieden werden. Verwenden Sie Sammlungen am besten nur für die Suche nach Zeichenfolgenschlüsseln oder für jede Iteration.
Ben McIntyre
4
Ich fand eine kürzere Contains: On Error Resume Next_ col(key)_Contains = (Err.Number = 0)
TWiStErRob
5
Vielleicht sollte die Funktion benannt werden ContainsKey; Jemand, der nur den Aufruf liest, kann ihn verwirren, um zu überprüfen, ob er einen bestimmten Wert enthält.
jpmc26
44

VBA verfügt nicht über eine interne Implementierung eines Wörterbuchs. In VBA können Sie jedoch weiterhin das Wörterbuchobjekt aus der MS Scripting Runtime Library verwenden.

Dim d
Set d = CreateObject("Scripting.Dictionary")
d.Add "a", "aaa"
d.Add "b", "bbb"
d.Add "c", "ccc"

If d.Exists("c") Then
    MsgBox d("c")
End If
Jarmo
quelle
29

Ein zusätzliches Wörterbuchbeispiel, das nützlich ist, um die Häufigkeit des Auftretens einzudämmen.

Außerhalb der Schleife:

Dim dict As New Scripting.dictionary
Dim MyVar as String

Innerhalb einer Schleife:

'dictionary
If dict.Exists(MyVar) Then
    dict.Item(MyVar) = dict.Item(MyVar) + 1 'increment
Else
    dict.Item(MyVar) = 1 'set as 1st occurence
End If

So überprüfen Sie die Frequenz:

Dim i As Integer
For i = 0 To dict.Count - 1 ' lower index 0 (instead of 1)
    Debug.Print dict.Items(i) & " " & dict.Keys(i)
Next i
John M.
quelle
1
Ein zusätzlicher Tutorial-Link ist: kamath.com/tutorials/tut009_dictionary.asp
John M
Dies war eine sehr gute Antwort und ich habe sie benutzt. Ich stellte jedoch fest, dass ich nicht wie Sie auf dict.Items (i) oder dict.Keys (i) in der Schleife verweisen konnte. Ich musste diese (Elementliste und Schlüsselliste) in separaten Variablen speichern, bevor ich in die Schleife eintrat, und diese Variablen dann verwenden, um zu den benötigten Werten zu gelangen. Like - allItems = companyList.Items allKeys = companyList.Keys allItems (i) Wenn nicht, würde ich die Fehlermeldung erhalten: "Die Prozedur" Eigenschaft lassen nicht definiert und die Prozedur "Eigenschaft abrufen" hat kein Objekt zurückgegeben ", wenn versucht wurde, auf Schlüssel (i) oder zuzugreifen Elemente (i) in der Schleife.
Raddevus
10

Aufbauend auf der Antwort von cjrh können wir eine Contains-Funktion erstellen, für die keine Beschriftungen erforderlich sind (ich verwende keine Beschriftungen).

Public Function Contains(Col As Collection, Key As String) As Boolean
    Contains = True
    On Error Resume Next
        err.Clear
        Col (Key)
        If err.Number <> 0 Then
            Contains = False
            err.Clear
        End If
    On Error GoTo 0
End Function

Für ein Projekt von mir habe ich eine Reihe von Hilfsfunktionen geschrieben, damit sich ein CollectionVerhalten eher wie ein Verhalten verhält Dictionary. Es erlaubt weiterhin rekursive Sammlungen. Sie werden feststellen, dass der Schlüssel immer an erster Stelle steht, da er obligatorisch war und in meiner Implementierung sinnvoller war. Ich habe auch nur StringSchlüssel verwendet. Sie können es zurück ändern, wenn Sie möchten.

einstellen

Ich habe dies in set umbenannt, da dadurch alte Werte überschrieben werden.

Private Sub cSet(ByRef Col As Collection, Key As String, Item As Variant)
    If (cHas(Col, Key)) Then Col.Remove Key
    Col.Add Array(Key, Item), Key
End Sub

Erhalten

Das errZeug ist für Objekte, da Sie Objekte mit setund Variablen ohne übergeben würden. Ich denke, Sie können einfach überprüfen, ob es sich um ein Objekt handelt, aber ich war unter Zeitdruck.

Private Function cGet(ByRef Col As Collection, Key As String) As Variant
    If Not cHas(Col, Key) Then Exit Function
    On Error Resume Next
        err.Clear
        Set cGet = Col(Key)(1)
        If err.Number = 13 Then
            err.Clear
            cGet = Col(Key)(1)
        End If
    On Error GoTo 0
    If err.Number <> 0 Then Call err.raise(err.Number, err.Source, err.Description, err.HelpFile, err.HelpContext)
End Function

Hat

Der Grund für diesen Beitrag ...

Public Function cHas(Col As Collection, Key As String) As Boolean
    cHas = True
    On Error Resume Next
        err.Clear
        Col (Key)
        If err.Number <> 0 Then
            cHas = False
            err.Clear
        End If
    On Error GoTo 0
End Function

Entfernen

Wirft nicht, wenn es nicht existiert. Stellt nur sicher, dass es entfernt ist.

Private Sub cRemove(ByRef Col As Collection, Key As String)
    If cHas(Col, Key) Then Col.Remove Key
End Sub

Schlüssel

Holen Sie sich eine Reihe von Schlüsseln.

Private Function cKeys(ByRef Col As Collection) As String()
    Dim Initialized As Boolean
    Dim Keys() As String

    For Each Item In Col
        If Not Initialized Then
            ReDim Preserve Keys(0)
            Keys(UBound(Keys)) = Item(0)
            Initialized = True
        Else
            ReDim Preserve Keys(UBound(Keys) + 1)
            Keys(UBound(Keys)) = Item(0)
        End If
    Next Item

    cKeys = Keys
End Function
Evan Kennedy
quelle
6

Das Skript-Laufzeitwörterbuch scheint einen Fehler zu haben, der Ihr Design in fortgeschrittenen Stadien ruinieren kann.

Wenn der Wörterbuchwert ein Array ist, können Sie die Werte der im Array enthaltenen Elemente nicht durch einen Verweis auf das Wörterbuch aktualisieren.

Kalidas
quelle
6

Ja. Für VB6 , VBA (Excel) und VB.NET

Matthew Flaschen
quelle
2
Sie können weitere Fragen lesen: Ich habe nach VBA gefragt: Visual Basic für Application, nicht für VB, nicht für VB.Net, nicht für eine andere Sprache.
1
fessGUID: andererseits solltest du mehr Antworten lesen! Diese Antwort kann auch für VBA verwendet werden (insbesondere für den ersten Link).
Konrad Rudolph
5
Ich gebe zu. Ich habe die Frage zu schnell gelesen. Aber ich habe ihm gesagt, was er wissen muss.
Matthew Flaschen
5
@Oorang, es gibt absolut keine Hinweise darauf, dass VBA zu einer Teilmenge der VB.NET-Backcompat-Regeln in Office wird. Stellen Sie sich vor, Sie versuchen, jedes jemals geschriebene Excel-Makro zu konvertieren.
Richard Gadsden
2
VBA ist eigentlich ein SUPERSET von VB6. Es verwendet dieselbe Kern-DLL wie VB6, fügt dann jedoch alle Arten von Funktionen für die spezifischen Anwendungen in Office hinzu.
David-W-Fenton
4

Wenn Sie aus irgendeinem Grund keine zusätzlichen Funktionen in Ihrem Excel installieren können oder möchten, können Sie auch Arrays verwenden, zumindest für einfache Probleme. Als WhatIsCapital geben Sie den Namen des Landes ein und die Funktion gibt Ihnen das Kapital zurück.

Sub arrays()
Dim WhatIsCapital As String, Country As Array, Capital As Array, Answer As String

WhatIsCapital = "Sweden"

Country = Array("UK", "Sweden", "Germany", "France")
Capital = Array("London", "Stockholm", "Berlin", "Paris")

For i = 0 To 10
    If WhatIsCapital = Country(i) Then Answer = Capital(i)
Next i

Debug.Print Answer

End Sub
user2604899
quelle
1
Das Konzept dieser Antwort ist solide, aber der Beispielcode wird nicht wie geschrieben ausgeführt. Jede Variable benötigt ein eigenes DimSchlüsselwort, Countryund Capitalmüssen als Varianten erklärt werden durch die Verwendung von Array(), isollte erklärt werden (und sein muss , wenn Option Explicitgesetzt), und der Schleifenzähler wird eine aus gebundenen Fehler werfen - sicherer Verwenden Sie UBound(Country)für den ToWert. Vielleicht ist es auch erwähnenswert, dass die Array()Funktion zwar eine nützliche Verknüpfung ist, aber nicht die Standardmethode zum Deklarieren von Arrays in VBA ist.
JCB