martes, 18 de octubre de 2022

VBA: Dimensiones de una Array

Dedicaremos un poco de tiempo a ver ciertas curiosidades al trabajar con Arrays en VBA, en concreto cómo se cuantifican las dimensiones al crear o cargar una Array.

Resulta curioso que al cargar un vector de valores desde un rango de celdas, se genere una array de dos dimensiones... (a pesar de ser solo una fila o columna de datos), o que al crear una Array de constantes, obtengamos una array unidimensional.

Por otra parte, la numeración o intervalo asignado a estas dimensiones puede variar, empezando desde 0 o desde 1, dependiendo de la procedencia de la Array... si bien, gran parte (NO todas) de las arrays se pueden homogeneizar y forzar para que comiencen por 1 o por 0 (0 to 10, o bien 1 to 11).
Basta definir al inicio del módulo la instrucción Option Base 0 o Option Base 1

Veamos y analicemos el siguiente código insertado en un módulo estándar:
Sub ArrayDimensiones()
'matriz de una dimensión 1 to 6
Dim arrCreada() As Variant
arrCreada = Array("ES", "FR", "DE", "PT", "UK", "IT")

'matriz de 'dos' dimensiones 1 to 6, 1 to 1 !!!
Dim arrDatos() As Variant
arrDatos = Hoja1.Range("C4:C9").Value

'y podemos convertirla en una nueva de 0 to 5
Dim txt As String, arrTmp As Variant
txt = Join(Application.Transpose(arrDatos), "|")
arrTmp = Split(txt, "|")

'Alternativamente, reducimos/quitamos una de las dimensiones...
Dim arrDatosNew(1 To 6) As Variant
For f = 1 To 6
    arrDatosNew(f) = Application.Transpose(Application.Index(arrDatos, f, 1))
Next f

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Unimos dos arrays de igual dimensión (vectores unidimensionales)
'y resulte una nueva array de 2-D
Dim arr2D(1 To 6, 1 To 2) As Variant
For f = 1 To 6
    arr2D(f, 1) = arrCreada(f - 1)
    arr2D(f, 2) = arrDatosNew(f)
Next f

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Y si probamos una Array de Arrays???
'OJO, tendremos una matriz Unidimensional,
'que contiene dos matrices Unidimensionales (de cualquier tipo)
Dim arrJagged() As Variant
ReDim arrJagged(1 To 2) As Variant
For a = 1 To 2  'número de arrays
    Select Case a
        Case Is = 1: matriz = arrCreada
        Case Is = 2: matriz = arrTmp
    End Select
    arrJagged(a) = matriz
Next a

Debug.Print arrJagged(2)(3)

End Sub


VBA: Dimensiones de una Array

Fijémonos en la ventana de variables locales cómo se ha dimensionado cada una de las arrays trabajadas...
Recuerda que estamos trabajando, por omisión, en Option base 0.
La primera de ellas:
arrCreada = Array("ES", "FR", "DE", "PT", "UK", "IT")
matriz de constantes, se ha creado con una dimensión, con un intervalo de elementos de 0 to 5
Parece lógico sea así, al ser un listado plano de valores...

La siguiente array:
arrDatos = Hoja1.Range("C4:C9").Value
procede de la carga de un rango de celdas, pero en concreto de un vector vertical (solo una columna de valores), sin embargo, se ha creado con dos dimensines, con intervalos de elementos de (1 to 6, 1 to 1)
Podríamos decir que está pensado para trabajar sobre rangos de varias filas por varias columnas...y que la excepción de ser un vector, la salva definiendo la segunda dimensión como (1 to 1)

Si el rango hubiera tenido dos columnas sería (1 to 2), tres columnas (1 to 3), etc...

Notemos que en este caso, los elementos empienzan por defecto en 1 y no en 0 como el caso anterior.

Vamos a 'transformar' esta última matriz-array, en una UNIdimensional.
Un truco para esto es emplear la combinación de funciones VBA JOIN-SPLIT, junto a la función Transpose:
Dim txt As String, arrTmp As Variant
txt = Join(Application.Transpose(arrDatos), "|")
arrTmp = Split(txt, "|")

El punto interesante es que, efectivamente hemos conseguido reducir el número de dimensiones de dos a una, pero lo curioso es que SPLIT retornará siempre un intervalo de (0 to n). :O

Una alternativa al método anterior sería el siguiente:
'Alternativamente, reducimos/quitamos una de las dimensiones...
Dim arrDatosNew(1 To 6) As Variant
For f = 1 To 6
arrDatosNew(f) = Application.Transpose(Application.Index(arrDatos, f, 1))
Next f

Con esta forma si controlamos en que valor 0 ó 1 empezará el intervalo de nuestra dimensión

Dos tips extra contenidos en la macro... cómo unir arrays.
Ya vimos en el blog algún método para unir rangos de celdas discontinuos (artículo uno o dos).

En este ejemplo veremos métodos alternativos. ;-)
El primero de ellos es algo rústico y demasiado rígido, pero funcional... Consiste en crear una nueva array con las dimensiones correctas, e ir añadiendo los diferentes elementos de las arrays originales:
'Unimos dos arrays de igual dimensión (vectores unidimensionales)
'y resulte una nueva array de 2-D
Dim arr2D(1 To 6, 1 To 2) As Variant
For f = 1 To 6
    arr2D(f, 1) = arrCreada(f - 1)
    arr2D(f, 2) = arrDatosNew(f)
Next f

el resultado una matriz bidimensional compuesta por los valores de las dos primeras arrays...

El segundo consiste en crear una Array de Arrays (en inglés se le conoce a esto como Jagged array matriz irregular o matriz escalonada... no se si de alguna otra forma, yo la bautizaría 'Array Frankestein').
Basicamente consiste en crear una Array con elementos que podrian ser cualquier cosa: rangos, constantes, arrays, ... y de cualquier tamaño o dimensión

Esto hace esta herramienta muy potente, pero a la vez puede resultar compleja de manejar...
Cómo conseguimos esta Array de arrays. Una forma muy básica podría ser:
'Y si probamos una Array de Arrays???
'OJO, tendremos una matriz Unidimensional,
'que contiene dos matrices Unidimensionales (de cualquier tipo)
Dim arrJagged() As Variant
ReDim arrJagged(1 To 2) As Variant
For a = 1 To 2  'número de arrays
    Select Case a
        Case Is = 1: matriz = arrCreada
        Case Is = 2: matriz = arrTmp
    End Select
    arrJagged(a) = matriz
Next a
'para recuperar el tercer elemento de la segunda Array cargada
Debug.Print arrJagged(2)(3)


Quizá este artículo parezca tonto o inutil, pero conocer las dimensiones de las arrays, así como su numeración de intervalos de los elementos, evitará muchos errores en nuestros códigos... y muchos quebraderos de cabeza ;-)

No hay comentarios:

Publicar un comentario

Nota: solo los miembros de este blog pueden publicar comentarios.