2012-01-13 22 views
35

El siguiente código me da el error 9 "subíndice fuera de rango". Quise declarar una matriz dinámica para que la dimensión cambie a medida que agrego elementos a ella. ¿Debo crear una "mancha" en la matriz antes de almacenar algo en ella como en JS?Poblar matrices dinámicas de VBA

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

Respuesta

47

en su bucle for use un Redim en la matriz como aquí:

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

¿Por qué lo harías * en * el ciclo? 'ReDim', y especialmente cuando agrega' Preserve', es un potencial asesino de rendimiento. Ya sabes cuántas veces va a iterar el ciclo, así que definitivamente hazlo fuera del ciclo. Entonces solo estás cambiando el tamaño de la matriz una vez, y no necesitas la 'Reserva '. –

+9

@CodyGray Tiene toda la razón si el tamaño final de la matriz ya está definido al ingresar al ciclo. Poner el Redim dentro del circuito sería un asesino de rendimiento. Sin embargo, asumí que el tamaño de la matriz no se determina al ingresar al ciclo. De lo contrario, la muestra completa no tiene ningún sentido ... – Fluffi1974

+3

Tiene * que definirse * al ingresar al ciclo. Tienes que definir el rango del bucle. Incluso si es una variable en lugar de una constante como '3', todavía está especificando un límite superior en la instrucción' For'. Úselo para inicializar dinámicamente el tamaño de la matriz. –

21

Sí, que busca la declaración ReDim, que asigna dinámicamente la cantidad necesaria de espacio en la matriz.

la siguiente declaración

Dim MyArray() 

declara una matriz sin dimensiones, por lo que el compilador no sabe lo grande que es y no puede almacenar cualquier cosa dentro de ella.

Pero se puede utilizar la instrucción ReDim cambiar el tamaño de la matriz:

ReDim MyArray(0 To 3) 

Y si necesita cambiar el tamaño de la matriz, mientras que preservar su contenido, puede utilizar la palabra clave Preserve junto con la declaración ReDim:

ReDim Preserve MyArray(0 To 3) 

Pero tenga en cuenta que tanto ReDim y en particular ReDim Preserve tienen un costo de rendimiento pesado. Intente evitar hacer esto una y otra vez en un ciclo si es posible; tus usuarios te lo agradecerán


Sin embargo, en el sencillo ejemplo que se muestra en su pregunta (si no es sólo una muestra de usar y tirar), no es necesario ReDim en absoluto. Sólo declarar la matriz con las dimensiones explícitas:

Dim MyArray(0 To 3) 
+3

+1 aunque agregaría que el límite inferior puede y en mi opinión debe especificarse explícitamente: 'ReDim MyArray (0 a 3)' –

+1

@Jean: Es un buen consejo. Mucha gente está mordida por el hecho de que VB (A) admite límites inferiores a 0. Ser explícito es siempre una buena práctica. –

9

Además de útiles comentarios de Cody vale la pena señalar que a veces no se sabe el tamaño de su matriz debe ser. Las dos opciones en esta situación son

  1. Creación de una matriz lo suficientemente grande como para manejar cualquier cosa que piense que serán lanzadas al que
  2. Sensible uso de Redim Preserve

El código siguiente es un ejemplo de una rutina que tendrá la dimensión myArray en línea con la variable lngSize, luego agregue elementos adicionales (igual al tamaño de la matriz inicial) mediante el uso de una prueba Mod cuando el límite superior esté a punto de ser excedido

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 
20

Primer póster de tiempo, lector de larga duración. Como mencionaron Cody y Brett, podría reducir la ralentización de VBA con el uso razonable de Redim Preserve. Brett sugirió Mod para hacer esto.

También puede usar un usuario definido Type y Sub para hacer esto.Considere el código de abajo:

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 

Para esto se necesitan ReDim Preserve sólo cuando el tamaño se ha duplicado. La variable miembro eSize realiza un seguimiento del tamaño de los datos reales de eElems. Este enfoque me ha ayudado a mejorar el rendimiento cuando la longitud final de la matriz no se conoce hasta el tiempo de ejecución.

Espero que esto ayude a los demás también.

+1

te das cuenta de que esta pregunta fue hecha (y respondida) hace 18 meses, ¿verdad? –

+15

¡sí! quería ofrecer una alternativa para otros lectores también. Me doy cuenta de que el operador ya ha aceptado la respuesta anterior. Gracias. – a505999

+0

Bueno, aquí hay un +1 para ti, ¡una alternativa interesante! :) –

8

veo muchos (todos) los mensajes por encima de depender de LBound/UBound exhorta pero potencialmente sin inicializar VBA matriz dinámica, lo que causa una muerte inevitable de aplicación ...

código errático:

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

código correcto:

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

... es decir, cualquier código donde Dim arr1() es seguido inmediatamente por LBound(arr1)/UBound(arr1) llamadas sin ReDim arr1(...) en el medio, se bloquea. La rotonda debe emplear un On Error Resume Next y comprobar el Err.Number justo después de la llamada LBound(arr1)/UBound(arr1) - debe ser 0 si el conjunto se inicializa; de lo contrario, no es cero. Debido a que existe una mala conducta incorporada de VBA, se necesita una mayor verificación de los límites de la matriz. Explicación detallada puede leer todo el mundo en Chip Pearson's website (que debe considerarse como una sabiduría humanidad tesoro de VBA ...)

Je, eso es mi primer post, creo que es legible.

+0

Ese comentario es todo codificado anteriormente, confiando en ubound/lbound es incorrecto – brettdj