Recientemente en grupos de .NET salió el tema de cómo se pueden redimensionar las dos dimensiones de una matriz de dos dimensiones sin perder los valores que ya se tienen almacenados en dicha matriz.
Si fuera una matriz de una dimensión no habría ningún problema, ya que la opción Preserve nos permite hacer precisamente eso:
myArray(0) = 2
myArray(1) = 4
myArray(2) = 6
myArray(3) = 8
ReDim Preserve myArray(5)
myArray(4) = 10
myArray(5) = 12
Así, este código no da ningún problema. Y tampoco lo da este otro:
myArray(0, 0) = 2
myArray(1, 0) = 4
myArray(2, 0) = 6
myArray(3, 0) = 8
ReDim Preserve myArray(3, 1)
myArray(0, 1) = 10
myArray(1, 1) = 12
myArray(2, 1) = 14
myArray(3, 1) = 1
Ya que aunque es una matriz de dos dimensiones, sólo estamos redimensionando la dimensión situada más a la derecha. En cambio si intentamos hacer esto que sigue:
myArray(0, 0) = 2
ReDim Preserve myArray(4, 1)
myArray(4, 1) = 10
Nos dará una excepción de tipo ArrayTypeMismatchException y nos dirá que…
‘ReDim’ sólo puede cambiar la dimensión situada más a la derecha
…porque un ReDim Preserve en una matriz de dos dimensiones sólo puede actuar sobre la última dimensión.
Para solventar esto podemos utilizar la siguiente función:
' Author: Albert Mata (www.albertmata.net)
' Date: 20081118
' Description: Simulates a ReDim Preserve action on 2-dimensions
' arrays, allowing to change not only the last dimension
' but both.
'--------------------------------------------------------------------
Public Function ReDimPreserve(ByVal M As Array, _
ByVal NewLimit0 As Integer, ByVal NewLimit1 As Integer) As Array
If NewLimit0 >= M.GetUpperBound(0) _
And NewLimit1 >= M.GetUpperBound(1) Then
Dim NewArray(NewLimit0, NewLimit1) As [Int32]
For i As Integer = 0 To M.GetUpperBound(0)
For j As Integer = 0 To M.GetUpperBound(1)
NewArray.SetValue(M.GetValue(i, j), i, j)
Next
Next
Return NewArray
Else
Return M
End If
End Function
De tal manera que ahora ante el siguiente código, donde creamos una matriz inicialmente de dimensiones (2,3) para luego redimensionarla a (4,5) y por tanto cambiando las dos dimensiones de la matriz, sin perder los valores que ya teníamos almacenados…
Dim myArray(2, 3) As [Int32]
Debug.Print("Storing value 12 in position [0,2]")
myArray.SetValue(12, 0, 2)
Debug.Print("Storing value 15 in position [2,3]")
myArray.SetValue(15, 2, 3)
Debug.Print("Upper bound for first dimension = " _
& (myArray.GetUpperBound(0)))
Debug.Print("Upper bound for second dimension = " _
& (myArray.GetUpperBound(1)))
Debug.Print("Value in position [0,2] = " _
& myArray.GetValue(0, 2).ToString)
Debug.Print("Value in position [2,3] = " _
& myArray.GetValue(2, 3).ToString)
Try
myArray.SetValue(24, 3, 4)
Debug.Print("I can store values in position [3,4]")
Catch ex As Exception
Debug.Print("I can't store values in position [3,4]")
End Try
Debug.Print("Changing array dimensions to [4,5]")
myArray = DirectCast(Me.ReDimPreserve(myArray, 4, 5), Integer(,))
Debug.Print("Upper bound for first dimension = " _
& (myArray.GetUpperBound(0)))
Debug.Print("Upper bound for second dimension = " _
& (myArray.GetUpperBound(1)))
Debug.Print("Value in position [0,2] = " _
& myArray.GetValue(0, 2).ToString)
Debug.Print("Value in position [2,3] = " _
& myArray.GetValue(2, 3).ToString)
Try
myArray.SetValue(24, 3, 4)
Debug.Print("I can store values in position [3,4]")
Catch ex As Exception
Debug.Print("I can't store values in position [3,4]")
End Try
…obtenemos esta salida en la Ventana Inmediato:
Storing value 12 in position [0,2]
Storing value 15 in position [2,3]
Upper bound for first dimension = 2
Upper bound for second dimension = 3
Value in position [0,2] = 12
Value in position [2,3] = 15
*** 'System.IndexOutOfRangeException' EXCEPTION ***
I can't store values in position [3,4]
Changing array dimensions to [4,5]
Upper bound for first dimension = 4
Upper bound for second dimension = 5
Value in position [0,2] = 12
Value in position [2,3] = 15
I can store values in position [3,4]
La función propuesta no es ni mucho menos perfecta. Tendría que hacerse más genérica, permitir redimensionar no dos sino N dimensiones, controlar mejor posibles errores y demás. Pero puede ser una primera aproximación para resolver situaciones de este tipo…
PD. También es posible utilizar el método Array.Copy en lugar de iterar por las dos dimensiones de la matriz, aunque en este caso lo he hecho así para que resulte más evidente el proceso.
Cuando toco .NET (cada vez menos) lo suelo hacer con C#, pero al final todo se puede aplicar ¿qué tal el uso de ArrayList cuando necesitas arrays con crecimiento?
Y cuando no también. A poco que vayas a darle algo de caña al array, para mi gusto el ArrayList ofrece mayores prestaciones y es, de hecho, la clase que suelo utilizar para colecciones. Pero surgió el tema este del Array bidimensional y me intrigó si se podría resolver…
PD. ¡Celebro ver que hoy sí te llegó el RSS!
Hola Albert, una preguntita, no tiene nada que ver con la entrada pero bueno… como haces para poner codigo con tabulaciones??? He googlead, pero no he encontrado nada.
Muchas gracias
@geek… dios, odio que me hagas esta pregunta. No, odio no, ODIO…
Es que la respuesta es muy cutre, es una chapuza soberana… pero verás, tenía que tabularlo sí o sí, y lo que encontraba por ahí (hay plugins que lo hacen más o menos) no me convencía nada por uno u otro motivo.
Así que me cree una miniaplicación (es un .exe sin instalación ni nada, a pelo) a la que le paso un texto, le doy a un botón y me lo convierte a entidades seguras (ampersand almohadilla 160 punto y coma, ya sabes), con lo cual lo hace absolutamente ilegible. De este modo, yo los posts los escribo en un simple bloc de notas, después los pego en el editor de WordPress y finalmente los bloques de codigo los paso previamente por esa aplicacioncita. Y listos. Una chapuza, sí, pero funciona bien. Bueno, también tengo creado un estilo css sobre la etiqueta div para que salga en azulito y con el recuadro, pero para lo de tabular mi truco es lo de convertir a entidades seguras.