web-dev-qa-db-fra.com

Remplissage de tableaux dynamiques VBA

Le code suivant me donne l'erreur 9 "indice en dehors de la plage". Je voulais déclarer un tableau dynamique afin que la dimension change à mesure que j'y ajoute des éléments. Dois-je créer un "spot" sur le tableau avant de stocker quelque chose comme dans JS?

Sub test_array()
    Dim test() As Integer
    Dim i As Integer
    For i = 0 To 3
        test(i) = 3 + i
    Next i
End Sub
43
sebastien leblanc

dans votre boucle for utilisez un Redim sur le tableau comme ici:

For i = 0 to 3
  ReDim Preserve test(i)
  test(i) = 3 + i
Next i
57
Fluffi1974

Oui, vous recherchez l'instruction ReDim, qui alloue dynamiquement la quantité d'espace requise dans le tableau.

La déclaration suivante

Dim MyArray()

déclare un tableau sans dimensions, ainsi le compilateur ne sait pas quelle est sa taille et ne peut rien y stocker.

Mais vous pouvez utiliser l'instruction ReDim pour redimensionner le tableau:

ReDim MyArray(0 To 3)

Et si vous avez besoin de redimensionner le tableau avec conservation son contenu, vous pouvez utiliser le mot clé Preserve avec l'instruction ReDim:

ReDim Preserve MyArray(0 To 3)

Mais notez que ReDim et particulièrement ReDim Preserve ont un coût de performance élevé. Essayez d'éviter de faire cela encore et encore dans une boucle si possible; vos utilisateurs vous remercieront.


Toutefois, dans l'exemple simple présenté dans votre question (s'il ne s'agit pas d'un exemple à jeter), vous n'avez pas du tout besoin de ReDim. Il suffit de déclarer le tableau avec des dimensions explicites:

Dim MyArray(0 To 3)
24
Cody Gray

Comme Cody et Brett l'ont mentionné, vous pouvez réduire le ralentissement de VBA avec une utilisation judicieuse de Redim Preserve. Brett a suggéré que Mod le fasse.

Vous pouvez également utiliser Type et Sub définis par l'utilisateur pour ce faire. Considérons mon code ci-dessous:

Public Type dsIntArrayType
   eElems() As Integer
   eSize As Integer
End Type

Public Sub PushBackIntArray( _
    ByRef dsIntArray As dsIntArrayType, _
    ByVal intValue As Integer)

    With dsIntArray
    If UBound(.eElems) < (.eSize + 1) Then
        ReDim Preserve .eElems(.eSize * 2 + 1)
    End If
    .eSize = .eSize + 1
    .eElems(.eSize) = intValue
    End With

End Sub

Ceci appelle ReDim Preserve Uniquement lorsque la taille a doublé. La variable membre eSize assure le suivi de la taille réelle des données de eElems. Cette approche m'a aidé à améliorer les performances lorsque la longueur de la matrice finale n'est pas connue jusqu'au moment de l'exécution.

J'espère que cela aide les autres aussi.

23
a505999

En plus des commentaires utiles de Cody, il est intéressant de noter que vous ne saurez parfois pas à quel point votre tableau doit être grand. Les deux options dans cette situation sont

  1. Créer un tableau assez grand pour gérer tout ce que vous pensez être projeté
  2. Sensible utilisation de Redim Preserve

Le code ci-dessous fournit un exemple de routine qui dimensionnera myArray conformément à la variable lngSize, puis ajoutera des éléments supplémentaires (égaux à la taille initiale du tableau) à l'aide de Mod tester chaque fois que la limite supérieure est sur le point d'être dépassée

Option Base 1

Sub ArraySample()
    Dim myArray() As String
    Dim lngCnt As Long
    Dim lngSize As Long

    lngSize = 10
    ReDim myArray(1 To lngSize)

    For lngCnt = 1 To lngSize*5
        If lngCnt Mod lngSize = 0 Then ReDim Preserve myArray(1 To UBound(myArray) + lngSize)
        myArray(lngCnt) = "I am record number " & lngCnt
    Next
End Sub
11
brettdj

Je vois beaucoup (tous) de messages ci-dessus qui s'appuient sur LBound/UBound fait appel à un tableau dynamique VBA non encore potentiellement initialisé, ce qui cause la mort inévitable de l'application ...

Code erratique:

Dim x As Long Dim arr1() As SomeType ... x = UBound(arr1) 'crashes

Code correct:

Dim x As Long Dim arr1() As SomeType ... ReDim Preserve arr1(0 To 0) ... x = UBound(arr1)

... c’est-à-dire tout code dans lequel Dim arr1() est immédiatement suivi de LBound(arr1)/UBound(arr1) sans appels de ReDim arr1(...), se bloque. Le rond-point doit employer un On Error Resume Next Et vérifier le Err.Number Juste après l'appel LBound(arr1)/UBound(arr1) - il devrait être 0 si le tableau est initialisé , sinon non nul. Comme il existe certains problèmes de comportement intégrés à VBA, une vérification supplémentaire des limites de array est nécessaire. Des explications détaillées peuvent être lues par tout le monde sur site Web de Chip Pearson (qui devrait être célébré comme un trésor de l'humanité de la sagesse de VBA ...)

Heh, c'est mon premier post, crois que c'est lisible.

10
Petr Pivonka