Debugging VBA code - appending values to array - vba

I am trying to use code of the following form to populate an array of x rows:
Dim myarray() As Variant
Dim string1 As String
Dim myarray_ubound As Integer
myarray_ubound = 0
For i = 1 to x
myarray_ubound = myarray_ubound + 1
ReDim Preserve myarray(1 To myarray_ubound, 1 To 2)
myarray(myarray_ubound,1) = i
myarray(myarray_ubound,2) = string1
Next i
However, when I run it it gets stuck after the first loop, telling me that a subscript is out of range. Debugging takes me to the ReDim command.
I used myarray_ubound as an alternative to calling the UBound function many times, but I have tried using this as well and I get the same error.
Can anyone spot what's gone wrong?

See: http://msdn.microsoft.com/en-us/library/aa266231.aspx
"If you use the Preserve keyword, you can resize only the last array dimension and you can't change the number of dimensions at all."
Try
ReDim Preserve myarray(1 To 2, 1 To myarray_ubound)
instead.

you can only redim the last element of the array (not the first), see below. As you know x I would suggest to redim your array with this value from the beginning.
Dim myarray() As Variant
Dim string1 As String
Dim myarray_ubound As Integer
myarray_ubound = 0
For i = 1 To 10
myarray_ubound = myarray_ubound + 1
ReDim Preserve myarray(1 To 2, 1 To myarray_ubound)
myarray(1, myarray_ubound) = i
myarray(2, myarray_ubound) = string1
Next i

Since you are declaring the variable as a Variant i don't see why you need to re-dim. (maybe related to memory issues, feel free to fill in).
I would suggest:
For i = 1 to 10
myArray(1, i-1) = i
myArray(2, i-1) = string1
next i

Related

Redim array after it is filled

Before filling my array I don't know how big it's going to get, but at a certain point (after all data has been inserted) I want to redim the array so there are no empty values in the end of the array. If I dim it as
valuesForSix() As Double
I cannot add values with
valuesForSix(0) = 9.654
Can somebody help me?
ReDim it!
Dim valuesForSix() As Double
ReDim valuesForSix(1) As Double
valuesForSix(0) = 9.654
valuesForSix(1) = 10.25
ReDim Preserve valuesForSix(10) As Double
Debug.Print valuesForSix(0)
Debug.Print valuesForSix(1)
The "Preserve" Keyword means that existing data is kept if the array size is increased.
You can use ReDim Preserve to redefine the array's dimensions. The Preserve keyword means the array retains all existing data. See this MSDN language reference for details.
This example initialises an empty array, then loops from 1 to 10 adding an extra element each time:
Dim v() As Double, i As Long
For i = 1 To 10
ReDim Preserve v(1 To i) As Double
v(i) = i
Next i
' v = [1 2 3 4 5 6 7 8 9 10]
You can extend the array by n elements using LBound and UBound:
Dim v(0 To 10) As Double, n As Long
n = 7
ReDim Preserve v(LBound(v) To UBound(v) + n)
' Now v is Double(0 to 17)

VBA dynamic array subscript error

I was trying to take some values from an excel sheet, to then process them, and I decided to use a Dynamic array, because I thought that it would be easier.
Dim Dias() As Variant
Dim Horas() As Variant
Dim Temp() As Variant
Dim Hum() As Variant
Sheets("Tfinal").Activate
Dias = Range("A2:A1745")
Horas = Range("B2:B1745")
Temp = Range("J2:J1745")
Sheets("Hfinal").Activate
Hum = Range("D2:D1745")
Dim TempNTemp() As Double
Dim NTemp() As Double
Dim NDias() As Variant
Dim NHoras() As Variant
Dim TempNHum() As Variant
Dim NHum() As Variant
Until here everything's fine, but the next line throws subscript out of range error. I'm really confused.
H = Horas(0)
Getting values from a range of cells always results in a 2-D array with a 1-based index. If you use a number of cells in a single column you still get a 1 to x, 1 to 1 array; if you use a number of cells in a single row you will get a 1 to 1, 1 to x array.
Your arrays are LBound/UBound/Ranked as follows:
Dias = Range("A2:A1745") 1 to 1744, 1 to 1
Horas = Range("B2:B1745") 1 to 1744, 1 to 1
Temp = Range("J2:J1745") 1 to 1744, 1 to 1
Hum = Range("D2:D1745") 1 to 1744, 1 to 1
So to access the first element of the first rank Horas array use one of the following:
Horas(1, 1)
Horas(LBound(Horas, 1), 1)
Truth be told, the default for the second rank is 1 so it is unnecessary. These will work just as well.
Horas(1)
Horas(LBound(Horas))
However, using that shorthand can cause confusion if you had more than a single second rank.
Horus = Range("A1:G1")
'first element
Horas(1, 1)
Horas(1, LBound(Horas, 2))
'second element
Horas(1, 2)
Setting a Watch on the array var will show you the dimensions as well as the contents.
I also use the following code within the procedure to visually see the array's dimensions in the Immediate window.
debug.print lbound(Horus, 1) & ":" & ubound(Horus, 1)
debug.print lbound(Horus, 2) & ":" & ubound(Horus, 2)
'results for Horus
1:1744
1:1

Type mimsatch when assigning result of MMult to an array variable

Apologies for the newbie question, but I couldn't find an answer when searching.
I'm fairly new to matrix manipulation in VBA. I keep on getting a type mismatch with the following code.
Sub matrixtest()
Dim matrix1() As Integer
Dim matrix2() As Integer
Dim matrix3() As Integer
Dim i, j, k As Integer
'populate matrix1
ReDim matrix1(3, 3)
For j = 1 To 3
For i = 1 To 3
matrix1(i, j) = Range("C5").Offset(i - 1, j - 1)
Next i
Next j
'populate matrix2
ReDim matrix2(3, 3)
For j = 1 To 3
For i = 1 To 3
matrix2(i, j) = Range("G5").Offset(i - 1, j - 1)
Next i
Next j
ReDim matrix3(3, 3)
matrix3 = Application.WorksheetFunction.MMult(matrix1, matrix2)
End Sub
If you replace the line
matrix3 = Application.WorksheetFunction.MMult(matrix1, matrix2)
By
Debug.Print TypeName(matrix3 = Application.WorksheetFunction.MMult(matrix1, matrix2))
The output is:
Variant()
Which can't be assigned to an Integer().
I would recommend replacing
Dim matrix1() As Integer
Dim matrix2() As Integer
Dim matrix3() As Integer
Dim i, j, k As Integer
by
Dim matrix1, matrix2, matrix3 As Variant 'note lack of ()
Dim i, j, k As Long 'Integer is borderline obsolete in VBA
Variants do a nice job of holding and passing arrays and tend to handle any needed type conversions automatically. When dealing with arrays in VBA, I tend to use them almost exclusively. For one thing, it makes it easy to load arrays from ranges.
Just use ReDim to make the variants hold arrays:
ReDim matrix1(1 to 3, 1 to 3) 'doesn't hurt to be explicit about lower bounds
ReDim matrix2(1 to 3, 1 to 3)
'load arrays...
'no need to redim matrix3, just:
matrix3 = Application.WorksheetFunction.MMult(matrix1, matrix2)
There is an even shorter way of doing what you are trying to do:
matrix1 = Range("C5:E7").Value
matrix2 = Range("G5:I7").Value
matrix3 = Application.WorksheetFunction.MMult(matrix1, matrix2)
In the above code you don't need to use any preliminary ReDim. When you want to load the values of a rectangular range into a variant in VBA, you don't need to loop, which is needlessly slow. Just assign the values in one fell swoop.

Efficient array scalar products in VBA

Generating two arrays, filled for example purposes with a column of ones. We know that scalar(dot) product will be the sum of the cross-products i.e. = dimension of the column. Why must we Dim and then ReDim the variables "xDim"? Is there a more efficient way to set up array computation?
Function OnesArray(xInput As Integer)
Dim xDim() As Variant
ReDim xDim(1 To xInput, 1)
Dim i As Long
For i = 1 To xInput Step 1
xDim(i, 1) = 1
Next i
Dim TempArrayA() As Variant
Dim TempArrayB() As Variant
ReDim TempArrayA(LBound(xDim) To UBound(xDim))
ReDim TempArrayA(LBound(xDim) To UBound(xDim))
TempArrayA = xDim
TempArrayB = xDim
OnesArray = Application.WorksheetFunction.SumProduct(TempArrayA, TempArrayB)
End Function
The output produced is: Input 1, 2, 3. Output 1, 2, 3. Expected output

automating a mundane task

I have a simple task that i need to automate.
I get a email in a very specific format from another application based on a trigger.
What i want is that out look "reads" the data in that email and compare two cells. if one cell is greater than the other, then i want the email forwarded to a specified address otherwise delete the email.
the folowing vba code was attempted, but gives a run time error. please guide
Sub GetLines()
Dim msg As Outlook.mailItem
Dim rows As Variant
Dim numberofColumns As Long
Dim numberofRows As Long
Dim headerValues As Variant
Dim headerRow() As String
Dim data() As String
Dim i As Long, j As Long
' get currently selected email
Set msg = ActiveExplorer.Selection.item(1)
' tokenize each line of the email
rows = Split(msg.Body, vbCrLf)
' calculate array size
numberofColumns = Len(rows(0)) - Len(Replace(rows(0), Chr(9), ""))
numberofRows = UBound(rows) + 1
' put header row into array
ReDim headerRow(1 To numberofColumns)
headerValues = Split(rows(0), Chr(9))
For i = 1 To numberofColumns
headerRow(i) = Trim$(headerValues(i - 1))
Next i
' calculate data array size
numberofRows = numberofRows - 1
' put data into array
ReDim data(1 To numberofRows, 1 To numberofColumns)
For i = 1 To numberofRows
For j = 1 To numberofColumns
data(i, j) = Trim$(Split(rows(i), Chr(9))(j - 1))
Next j
Next i
End Sub
Your code makes too many unnecessary assumptions about the data and will give errors most of the time. Firstly you need to use F8 to step through the code to isolate the error in a particular line.
I suggest you change
Dim data() As String
to
Dim data As Variant
data = Array()
I'm not an expert in how VBA manages memory but I know that I get a lot less grief when I make things variants.
You are most likely to have a problem here:
For i = 1 To numberofRows
For j = 1 To numberofColumns
data(i, j) = Trim$(Split(rows(i), Chr(9))(j - 1))
Next j
Next i
What if not every row is perfectly formed?
Instead, try this:
For i = 1 To numberofRows
For j = 1 To Ubound(Split(rows(i), Chr(9))) + 1
data(i, j) = Trim$(Split(rows(i), Chr(9))(j - 1))
Next j
Next i
This allows your code to "survive" a blank line or some other error in the data.