web-dev-qa-db-fra.com

En boucle dans un Scripting.Dictionary en utilisant un numéro d’index

Semblable à ce problème , lors de l'utilisation d'un objet Scripting.Dictionary dans VBA, le résultat du code ci-dessous est inattendu.

Option Explicit

Sub test()

    Dim d As Variant
    Dim i As Integer
    Dim s As String
    Set d = CreateObject("Scripting.Dictionary")

    d.Add "a", "a"
    Debug.Print d.Count ' Prints '1' as expected

    For i = 1 To d.Count
        s = d.Item(i)
        Debug.Print s ' Prints ' ' (null) instead of 'a'
    Next i

    Debug.Print d.Count ' Prints '2' instead of '1'

End Sub

En utilisant un indice de base zéro, le même résultat est atteint:

For i = 0 To d.Count - 1
    s = d.Item(i)
    Debug.Print s
Next i

En regardant l’objet, je constate qu’il contient deux éléments. La clé du nouvel élément ajouté est 1, telle que ajoutée à partir de i. Si j'augmente cette boucle à un nombre plus élevé, le nombre d'éléments du dictionnaire est augmenté, une fois pour chaque boucle.

J'ai testé cela dans Office/VBA 2003, 2010 et 2013. Tous présentent le même comportement, et j'attends que d'autres versions (2007) le fassent également.

Je peux contourner ce problème avec d'autres méthodes de bouclage, mais cela m'a pris au dépourvu lorsque j'essayais de stocker des objets et que l'erreur object prévue se produisait sur la ligne s = d.Item(i).

Pour mémoire, je sais que je peux faire des choses comme celle-ci:

For Each v In d.Keys
    Set o = d.item(v)
Next v

Mais je suis plus curieux de savoir pourquoi je n'arrive pas à parcourir les éléments par numéro.

37
Gaffi

Selon la documentation de la propriété Item :

Définit ou retourne un élément pour une clé spécifiée dans un objet Dictionary.

Dans votre cas, vous n'avez pas d'élément dont la clé est 1, ce qui fait:

s = d.Item(i)

crée en réalité une nouvelle paire clé/valeur dans votre dictionnaire et la valeur est vide car vous n'avez pas utilisé l'argument optionnel newItem.

Le dictionnaire a également la méthode Items qui permet de parcourir les index:

a = d.Items
For i = 0 To d.Count - 1
    s = a(i)
Next i
37
assylias

Ajouter à la réponse de assylias - assylias nous montre que D.ITEMS est une méthode qui renvoie un tableau. Sachant cela, nous n’avons pas besoin du tableau variant a(i) [Voir la mise en garde ci-dessous]. Nous devons juste utiliser la syntaxe de tableau appropriée.

For i = 0 To d.Count - 1
    s = d.Items()(i)
    Debug.Print s
Next i()

KEYS fonctionne de la même manière

For i = 0 To d.Count - 1
    Debug.Print d.Keys()(i), d.Items()(i)
Next i

Cette syntaxe est également utile pour la fonction SPLIT, ce qui peut aider à rendre cela plus clair. SPLIT renvoie également un tableau avec des limites inférieures à 0. Ainsi, ce qui suit affiche "C".

Debug.Print Split("A,B,C,D", ",")(2)

SPLIT est une fonction. Ses paramètres sont dans le premier ensemble de parenthèses. Les méthodes et les fonctions utilisent toujours le premier ensemble de parenthèses pour les paramètres, même si aucun paramètre n'est nécessaire. Dans l'exemple, SPLIT renvoie le tableau {"A", "B", "C", "D"}. Comme il retourne un tableau, nous pouvons utiliser un deuxième ensemble de parenthèses pour identifier un élément dans le tableau retourné, comme nous le ferions pour n'importe quel tableau.

Caveat : Cette syntaxe plus courte n'est peut-être pas aussi efficace que d'utiliser le tableau variant a() lors de l'itération dans le dictionnaire entier, car la syntaxe plus courte appelle la méthode Items du dictionnaire à chaque itération. La syntaxe la plus courte est préférable pour cueillir un seul élément par numéro dans un dictionnaire. 

37
Craig Hatmaker

Utiliser la méthode d.Keys()(i) est une très mauvaise idée, car à chaque appel, il créera un nouveau tableau (vous obtiendrez une réduction de vitesse significative).

Voici un analogue de Scripting.Dictionary appelé "Table de hachage" de @ "The Trick", qui supporte cet énumérateur: http://www.cyberforum.ru/blogs/354370/blog2905.html

Dim oDict As clsTrickHashTable

Sub aaa()
    Set oDict = New clsTrickHashTable

    oDict.Add "a", "aaa"
    oDict.Add "b", "bbb"

    For i = 0 To oDict.Count - 1
        Debug.Print oDict.Keys(i) & " - " & oDict.Items(i)
    Next
End Sub
0
Alex Dragokas