web-dev-qa-db-fra.com

VBA a-t-il une structure de dictionnaire?

VBA a-t-il une structure de dictionnaire? Comme clé <> valeur tableau?

252
fessGUID

Oui.

Définissez une référence au runtime MS Scripting ('Microsoft Scripting Runtime'). Selon le commentaire de @ regjo, allez dans Outils-> Références et cochez la case "Microsoft Scripting Runtime".

References Window

Créez une instance de dictionnaire en utilisant le code ci-dessous:

Set dict = CreateObject("Scripting.Dictionary")

ou

Dim dict As New Scripting.Dictionary 

Exemple d'utilisation:

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

N'oubliez pas de régler le dictionnaire sur Nothing lorsque vous avez fini de l'utiliser.

Set dict = Nothing 
326
Mitch Wheat

VBA a l'objet collection:

    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")

L'objet Collection effectue des recherches à base de clé en utilisant un hachage, donc c'est rapide.


Vous pouvez utiliser une fonction Contains() pour vérifier si une collection particulière contient une clé:

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

Edit 24 June 2015: Plus court Contains() grâce à @TWiStErRob.

Edit 25 September 2015: Ajouté Err.Clear() grâce à @scipilot.

170
Caleb Hattingh

VBA ne dispose pas d'une implémentation interne d'un dictionnaire, mais vous pouvez toujours utiliser l'objet dictionnaire à partir de MS Scripting Runtime Library.

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
42
Jarmo

Un exemple de dictionnaire supplémentaire utile pour contenir la fréquence des occurrences.

En dehors de la boucle:

Dim dict As New Scripting.dictionary
Dim MyVar as String

Dans une boucle:

'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

Pour vérifier la fréquence:

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
29
John M

En s'appuyant sur réponse de cjrh , nous pouvons créer une fonction Contains ne nécessitant aucune étiquette (je n'aime pas utiliser les étiquettes).

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

Pour un de mes projets, j’ai écrit un ensemble de fonctions d’aide permettant de faire en sorte que Collection se comporte davantage comme un Dictionary. Il autorise toujours les collections récursives. Vous remarquerez que Key vient toujours en premier parce que c'était obligatoire et plus logique dans mon implémentation. J'ai également utilisé uniquement les touches String. Vous pouvez le changer si vous le souhaitez.

Ensemble

J'ai renommé ceci pour le définir car il écrasera les anciennes valeurs.

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

Get

La substance err concerne les objets puisque vous transmettriez des objets à l'aide de set et des variables sans. Je pense que vous pouvez simplement vérifier si c'est un objet, mais j'étais pressé par le temps.

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

A

La raison de ce post ...

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

Retirer

Ne jette pas si ça n'existe pas. Veille simplement à ce qu'il soit supprimé.

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

Clés

Obtenez un tableau de clés.

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
9
Evan Kennedy

Oui. Pour VB6 , VBA (Excel) et VB.NET

6
Matthew Flaschen

Le dictionnaire d'exécution de script semble contenir un bogue qui peut ruiner votre conception à un stade avancé.

Si la valeur du dictionnaire est un tableau, vous ne pouvez pas mettre à jour les valeurs des éléments contenus dans le tableau via une référence au dictionnaire.

6
Kalidas

Si, pour une raison quelconque, vous ne pouvez pas installer de fonctionnalités supplémentaires dans votre Excel ou ne le souhaitez pas, vous pouvez également utiliser des tableaux, du moins pour les problèmes simples. En tant que WhatIsCapital, vous mettez le nom du pays et la fonction vous renvoie son capital.

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
4
user2604899

Tous les autres ont déjà mentionné l'utilisation de la version scripting.runtime de la classe Dictionary. Si vous ne parvenez pas à utiliser cette DLL, vous pouvez également utiliser cette version, ajoutez-la simplement à votre code.

https://github.com/VBA-tools/VBA-Dictionary/blob/master/Dictionary.cls

Il est identique à la version de Microsoft.

3