How do I resolve this scope issue in VB .NET? - scope

I have a code structure something like this:
For row = 1 To numRows
Dim arrayIndex As Integer = 0
For column As Integer = startColumn To endColumn
' whatever code
arrayIndex = arrayIndex + 1
Next
Next
Dim arrayIndex As Integer = 0
For column As Integer = startColumn To endColumn
' whatever code
arrayIndex = arrayIndex + 1
Next
Not exactly the code, so I don't really need suggestions about refactoring, but my problem is this - with this code I get a compiler error for the first Dim arrayIndex As Integer = 0 - "Variable 'arrayIndex' hides a variable in an enclosing block." As far as I can tell, arrayIndex is local to the first for loop and shouldn't exist by the time we reach the second loop. If I try to change the second declaration of arrayIndex to arrayIndex = 0, I get the error "Name 'arrayIndex' is not declared", as I expected. So is it visible, or not? Does this have something to do with the Dim keyword? Any suggestions of how to get around this, other than naming the second index variable something else?

So is it visible, or not?
The other way round. The outer variable is visible in the inner scope. It cannot be accessed because it has not yet been declared and hence its lifetime has not yet started. But the name already exists in the scope.
Does this have something to do with the Dim keyword?
Not really, it is just a limitation of how scopes work in VB. A variable exists in scope even before its lifetime started. Since its name is carried over to nested scopes, no other variable there can have the same name.

Why not just move it outside the loop and reset it after the first [one] ?

Just like #shadeMe said, DIM it outside, assign it inside
Dim arrayIndex As Integer
For row = 1 To numRows
arrayIndex = 0
For column As Integer = startColumn To endColumn
' whatever code
arrayIndex = arrayIndex + 1
Next
Next
arrayIndex = 0
For column As Integer = startColumn To endColumn
' whatever code
arrayIndex = arrayIndex + 1
Next

Related

call dictionary from one sub to another in VBA

I'm stuck with a problem when I'm working with a dictionary in VBA. The reason why I want to work with a dictionary and a do-while loop is because I have variables with different length, that I want to loop through.
First I want to give the dic keys and and items.
The reason why I skip one col for each loop is because each series has a col with dates and then a col with prices. If it is possible I want to capture the dates that match the prices in the same dictionary.
Sub opg1(dicSPX As Object)
Dim i As Integer, m As Integer
Dim varColLeng As Variant
Set dicSPX = CreateObject("Scripting.Dictionary")
m = 10
Sheets("Data").Activate
ReDim intCol(1 To nCol)
'opretter dictionary
ReDim n(1 To m)
Do While n <> ""
For i = 1 To mn
' redim preserve IntColLen
dicSPX.Add Cells(1, 2 + ((i - 1) * 2)).Value, Range(Cells(9, 2 + ((i - 1) * 2)), Cells(n, 2 + ((i - 1) * 2))).Value
Next i
Loop
End Sub
then I want to execute a procedure for all keys in my dic. I want to compute returns in different time series.
However, when I call the dic, to the sub Returns() I get an error (Compile error: Variable not defined). I'm new to dictionaries and I probably missed a small detail.
Sub Returns()
Call opg1(dicSPX)
Dim dicSPX As New Scripting.Dictionary
Dim varKey As Variant, varArr As Variant
For Each varKey In dicSPX
varArr = dicSPX(varKey)
For i = LBound(varArr, 1) To UBound(varArr, 1)
For j = LBound(varArr, 2) To UBound(varArr, 2)
' varReturns(i,j) = compute the return here
Next
Next
Next
Any suggestions? I hope the question is clear.
Thank you
It should be clear which variable is not defined by the line which is highlighted after the error is thrown. At a quick glance I can tell that you didn't define i or j:
Sub Returns()
Dim dicSPX As New Scripting.Dictionary
opg1(dicSPX)
Dim varKey As Variant
For Each varKey In dicSPX
Dim varArr As Variant
varArr = dicSPX(varKey)
Dim i As Long
For i = LBound(varArr, 1) To UBound(varArr, 1)
Dim j As Long
For j = LBound(varArr, 2) To UBound(varArr, 2)
varReturns(i,j) = compute the return here
Next
Next
Next
End Sub
You should always explicitly define each variable, one line at a time, right before they are used. This way the declaration is always near the usage so you can see it on the same page.
You should also make opg1 a function that returns a dictionary. That would clarify your intent. Passing a variable by reference makes it harder to tell how the program works to the reader.

User defined type variable

I am trying to write 3 user defined type variables which need to be associated with each other like this;
Type tdrivers
Strfirstname as string
Strsurname as string
Intage as integer
End type
Type Tcars
Strmake as string
Strmodel as string
Lngcc as long
Driverid() as tdrivers
End type
Type T_Race
Strlocation as string
DteRacedate as date
IntYear as integer
CarsID() as Tcars
End Type
Sub CreateRace()
Dim myrace() as T_Race
'Variables to hold integer 'values at runtime
Dim A as integer
Dim B as integer
Dim C as integer
'this line redims myrace ok
Redim myrace(A)
'This line doesn't do anything
'When I try to redim the 'carsID() array nested inside 'the myrace(A) like so;
Redim myrace(A).carsID(B)
'This line obviously does 'nothing either
Redim myrace(A).CarsID(B).driverid(C)
I need to be able to assign races to the myrace() array and then assign cars to each race they have taken part in and then drivers to cars they have driven. So the carsID() must be nested within myrace() and driverid() nested within carsID()
Once I know how to redim carsID() in can then redim Driverid() which is nested further within.
If I make all the arrays fixed with a constant value such as 8 then the sub runs ok and all races, cars and drivers are nested correctly. Its the redim on nested dynamic arrays that is failing. Hope this makes sense. Can anyone help. Thanks
The point is that you have to ReDim every sub-array individually. The following example initializes all sub arrays and prints them at the end:
Sub Example()
Dim i As Integer
Dim j As Integer
Dim k As Integer
ReDim myRace(5)
For i = 1 To 5
ReDim myRace(i).CarsID(5)
For j = 1 To 5
ReDim myRace(i).CarsID(j).Driverid(5)
For k = 1 To 5
myRace(i).CarsID(j).Driverid(k).Strfirstname = Chr(k + Asc("a")) & Str(i) & Str(j) & Str(k)
Next k
Next j
Next i
' Now print it
For i = 1 To 5
For j = 1 To 5
For k = 1 To 5
Debug.Print myRace(i).CarsID(j).Driverid(k).Strfirstname
Next k
Next j
Next i
End Sub

How to fix Left, Mid problems? I get error "Type Mismatch 13" on the line with Left

I'm reading strings from cells and separate them by the beginning of their name. I'm using Left function and compare it to beginning, but I get
Error 13 "Type Mismatch"
I found something on the internet and here like VBA Left(str, 2) but it doesn't work either.
Sub start()
Dim strprj As String
Dim LastRow, radek As Long
LastRow = Worksheets("PROJECT_OFFERSNDA").Range("A1").CurrentRegion.Rows.Count
For radek = 0 To LastRow - 1
strprj = Worksheets("PROJECT_OFFERS-NDA").Range("A1").Offset(radek)
If Left(strprj, 2) = "\\" Then
MsgBox Str
End If
Next radek
End Sub
You can't get a type mismatch on the Left function call with that code, because strprj is guaranteed to be a String:
Dim strprj As String
If you're getting a type mismatch error, it must be on this line:
strprj = Worksheets("PROJECT_OFFERS-NDA").Range("A1").Offset(radek)
Because that instruction is reading a Variant and implicitly coercing it into a String - this may succeed if the cell contains any value that can be represented as a string, or fail with a type mismatch otherwise.
That would happen if the cell you're reading from contains an error value, e.g. #VALUE!, #NA, or #REF!.
The solution is to avoid reading cells into anything other than a Variant.
Dim cellValue As Variant
cellValue = Worksheets("PROJECT_OFFERS-NDA").Range("A1").Offset(radek).Value
And then to validate what we're looking at:
If Not IsError(cellValue) Then
strprj = Left(cellValue, 2)
'...
End If
There are other issues:
LastRow = Worksheets("PROJECT_OFFERSNDA").Range("A1").CurrentRegion.Rows.Count
This isn't necessarily reliable. First, you don't want to be dereferencing that sheet every time you need it. Pull it into a local variabel:
Dim ws As Worksheet
Set ws = ActiveWorkbook.Worksheets("PROJECT_OFFERSNDA") '<~ ThisWorkbook? another workbook? Be specific!
This is also a potential issue:
Dim LastRow, radek As Long
radek is a Long integer, ....and LastRow is an implicit Variant/Empty. And, you don't need radek for another couple of lines, so, declare it when you need it. Getting the last row is usually more reliable with Range.End, used from the bottom of the sheet:
Dim lastRow As Long
lastRow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
Dim radek As Long '<~ what's wrong with "currentRow"? why "radek"?
For radek = 0 To lastRow - 1
'...
Next
That loop is confusing. Range.Offset is confusing. You need to know which row to look at to debug and make sure the data is ok, but nothing in your code points directly to that row number.
Dim currentRow As Long
For currentRow = 2 To lastRow
Dim cellValue As Variant
cellValue = ws.Cells(currentRow, 1).Value
If Not IsError(cellValue) Then
Dim projectPath As String '<~ "strprj" wants more vowels, and "str" prefix is irrelevant!
projectPath = cellValue
If Left(projectPath, 2) = "\\" Then
MsgBox projectPath
End If
Else
Debug.Print "Row " & currentRow & " contains an error, cannot process."
End If
Next

Array already dimentioned/subscript out of range

If I set an array as dynamic, I get a subscript out of range error. If I set it as static, I get an array already dimentioned error. What am I missing?
Dim arrTrips() As String 'dynamic
Dim arrTrips(1 To 99) As String 'static
...
For i = 2 To lastrow
If .Cells(i, "C").Value2 = Target.Value2 Then
ReDim Preserve arrTrips(UBound(arrTrips) + 1) <-- error here
arrTrips(UBound(arrTrips)) = .Cells(i, "M").Value2
Debug.Print arrTrips(UBound(arrTrips))
End If
Next
Edit: adding more context. I am adding items to the array inside a loop.
LOL I fixed it. It's so stupid. All I did was add ReDim arrTrips(1) As String right below Dim arrTrips() As String
As mentioned, you're intializing an empty array, the Ubound function fails on that. There is no built-in method to test whether an array is "empty" so you need to either use error-trapping or a UDF that encapsulates error-trapping in order to determine whether the array has no dimensions.
Alternatively, since you know at the onset that the array is empty, you can simply ReDim it to something, and then you can later ReDim Preserve it within your loop.
Sub staticArra()
Dim arrTrips() As String
ReDim arrTrips(1 To 1) As String
Dim i As Integer
For i = 1 To 10
ReDim Preserve arrTrips(LBound(arrTrips) To UBound(arrTrips) + 1) As String
Next
End Sub
The second one, using ReDim arrTrips(1 to 99) fails to compile, by design.
https://learn.microsoft.com/en-us/office/vba/Language/Reference/user-interface-help/redim-statement
The ReDim statement is used to size or resize a dynamic array that has already been formally declared by using a Private, Public, or Dim statement with empty parentheses (without dimension subscripts).
But the ideal approach (in terms of conserving lines & avoiding redundant calls) would be to instantiate your array using ReDim instead of Dim, with a subscript. You can ReDim Preserve this:
Sub f()
ReDim arrTrips(0) As String
Dim i As Integer
For i = 1 To 10
If (i - 1) > UBound(arrTrips) Then
ReDim Preserve arrTrips(i)
End If
Next
End Sub
In short: give your array an upper bound to begin with.
Declaration 1
Dim arrTrips() As String
This is dynamic but its empty. When you do a Ubound on this, compiler will not find a size and will throw an error. Below will work since it is not derived from your arrTrips array
ReDim Preserve arrTrips(10)
Declaration 2:
Dim arrTrips(1 To 99) As String
This being static array, it can only be dimensioned once and will throw an error if you try to redimesion it.
You can use a counter:
Dim arrTrips() As String
...
Dim k As Long
k = 1
For i = 2 To lastrow
If .Cells(i, "C").Value2 = Target.Value2 Then
ReDim Preserve arrTrips(1 to k)
arrTrips(k) = .Cells(i, "M").Value2
Debug.Print arrTrips(k)
k = k + 1
End If
Next
You can also use COUNTIF to set the size of the array before the loop:
Dim arrTrips() As String
...
Dim k As Long
k = Application.CountIf(.Range("C:C"), Target.Value2)
ReDim arrTrips(1 To k)
Dim j As Long
j = 1
For i = 2 To lastrow
If .Cells(i, "C").Value2 = Target.Value2 Then
arrTrips(j) = .Cells(i, "M").Value2
Debug.Print arrTrips(j)
j = j + 1
End If
Next

Using multiple occurrences of the same custom function in Excel

I'm writing a custom function with the goal of performing multiplications of multiple arrays containing complex numbers in Excel. As an example, I have these 3 arrays defined on my Excel worksheet:
MATZ1:
0.99995021721684+0.0172827928509254i -0.040624595514759-0.000234038263707613i
0.00490168593281832-0.850839538828158i 0.99995021721684+0.0172827928509254i
MATZ2:
0.996262283629251+0.149720806155247i -0.952677113437938-0.0475875415517989i
0.0156809592632005-0.313924412139317i 0.996262283629251+0.149720806155247i
MATZ3:
0.998558978121825+0.0929765994909912i -0.128534373384092-0.00398493017536902i
0.0448308688243513-1.44602474297277i 0.998558978121825+0.0929765994909912i
And I have written the following function in VBA:
Function IMATMULT(rng1 As Range, rng2 As Range) As Variant
Dim i As Integer
Dim j As Integer
Dim l As Integer
Dim temp As String
Dim NumColumns As Variant
Dim NumRows As Variant
Dim NumRows2 As Variant
NumRows = rng1.Rows.Count - 1
NumColumns = rng2.Columns.Count - 1
If (rng1.Columns.Count = rng2.Rows.Count) Then
NumRows2 = rng1.Columns.Count
Else
IMATMULT = "non compatible arrays"
Exit Function
End If
Dim matrix() As String
ReDim matrix(NumRows, NumColumns)
For i = 0 To NumRows
For j = 0 To NumColumns
temp = "0"
For l = 1 To NumRows2
temp = WorksheetFunction.ImSum(temp, WorksheetFunction.ImProduct(rng1(i + 1, l).Value, rng2(l, j + 1).Value))
Next l
matrix(i, j) = temp
Next j
Next i
IMATMULT = matrix()
End Function
Which is able to multiply two complex arrays together. When I write the folowing code line in Excel, I get the correct result:
However, when I try to multiply more than two arrays, I get an error result:
I know I could just simply do the multiplication one array at a time, but I'd like to know why using my function twice doesn't work, as my current knowledge of VBA doesn't allow me to understand the reason for it.
Many thanks.
EDIT1:
It would seem the problem is that the output from this function is a different format than the input. To work as I need it, this function needs to accept an array of text as input. However, I have no idea how to pass such an array into a function, any help would be welcome.

Resources