User defined type variable - excel

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

Related

Random pick cycle from an array in VBA

I'm new in VBA. I want to make a random pick cycle like that:
Let's say I have seven elements in an array(1,2,3,4,5,6,7), each time when I pick one element from the array, the total number of elements will decrease by 1. After picking every element, the array will be reset to what I initially defined (1,2,3,4,5,6,7) and do the random pick cycle again.
The result of every cycle should be different.
Is it possible to do that in VBA?
Here's a stateful function that does what you described each time it is called.
Option Base 1
Dim digits, NLeft
Function RemoveDigit() as Integer
Dim Element as Integer
If IsEmpty(digits) or NLeft = 0 Then
digits = array(1,2,3,4,5,6,7)
NLeft = 7
End If
Element = WorksheetFunction.RandBetween(1,NLeft)
RemoveDigit = digits(Element)
digits(Element) = digits(NLeft)
digits(NLeft) = RemoveDigit
NLeft = NLeft - 1
End Function
It uses a well known algorithm to arrange digits in a random order. Basically you choose to swap a random element number with the last element. Then you repeat it on an n - 1 sized array, making it a tail-recursive algorithm (although this implementation of it is not recursive).
Delete this if you want to, but here is a suggestion for a test sub:
Sub TestRemoveDigit()
NLeft = 0
For i = 1 To 7
d = RemoveDigit()
Debug.Print (d)
Next i
End Sub
I think this should do what you're asking for:
Option Explicit
Global vCurrentArray As Variant
Sub ResetArray()
vCurrentArray = Array(1, 2, 3, 4, 5, 6, 7)
End Sub
Sub RemoveElementWithIndex(lIndex As Long)
Dim vTemp() As Variant '* Change the type as needed
Dim lLBound As Long: lLBound = LBound(vCurrentArray)
Dim lUBound As Long: lUBound = UBound(vCurrentArray)
Dim i As Long, v As Variant
Dim blSkipped As Boolean
If lLBound = lUBound Then '* only 1 element
Call ResetArray
Else
ReDim vTemp(lLBound To lUBound - 1)
i = lLBound
For Each v In vCurrentArray
If i <> lIndex Or blSkipped Then
vTemp(i) = v
i = i + 1
Else
blSkipped = True
End If
Next v
vCurrentArray = vTemp
End If
End Sub
Function GetRandomElement() As Variant '* Change the type as needed
Dim lRandomIndex As Long
lRandomIndex = WorksheetFunction.RandBetween(LBound(vCurrentArray), UBound(vCurrentArray))
GetRandomElement = vCurrentArray(lRandomIndex)
RemoveElementWithIndex lRandomIndex
End Function
Sub TestCycles()
Dim lCycle As Long
Dim i As Long
ResetArray
For lCycle = 1 To 3
Debug.Print
For i = 1 To 7
Debug.Print "Cycle: " & lCycle, "i: " & i, "Random Elem: " & GetRandomElement
Next i
Next lCycle
End Sub
Note: There're many ways of achieving the end result. The above is almost a literal translation of your post.
We can not remove a random element from an array. We can redim array to remove last element(s). If you want to remove random element, you can use collection instead like ..
Option Explicit
Sub RemoveRandom()
Dim coll As Collection, cl As Variant, i As Long, j As Long
Set coll = New Collection
For Each cl In Range("A1:A7")
coll.Add cl.Value
Next cl
For j = 1 To coll.Count
i = WorksheetFunction.RandBetween(1, coll.Count)
Debug.Print coll(i)
coll.Remove (i)
Next j
End Sub

Visual Basic query (excel)

I just started a new job and am trying to use VBA within excel to store values within a multidimensional array.
so I first want to search through a column for values and as they occur, i want to store them once, and assign a product number that will increment each time it encounters a new unique String value in the column. then just print the number to the cell and only change it as it increments/
the values will be Strings, (product styles). the product number will be a number.(int)
will it be possible to do this in VBA for excel. I read online you can't use different data types in the same array.
sorry new to VBA and any help would be great. if i would be better off to use a normal function in excel let me know.
You can declare you array as Variant and then you can store different types in the Array. For example
Sub test()
Dim arr() As Variant
Dim i As Long
ReDim arr(0 To 1)
arr(0) = "hi"
arr(1) = 1
For i = LBound(arr, 1) To UBound(arr, 1)
Debug.Print arr(i)
Debug.Print VarType(arr(i))
Next
End Sub
This prints in the immediate window the following
hi
8 ' This corresponds to the type String
1
2 ' This corresponds to the type Integer
EDIT
As per the question, yes you can have different types of arrays as entries in an Variant-Array. For example:
Sub test2()
Dim arr As Variant
Dim str_a(3) As String
Dim lng_a(8) As Long
Dim i As Long, j As Long
For i = LBound(str_a, 1) To UBound(str_a, 1)
str_a(i) = "hi " & i
Next
For i = LBound(lng_a, 1) To UBound(lng_a, 1)
lng_a(i) = i
Next
ReDim arr(0 To 1)
arr(0) = str_a
arr(1) = lng_a
For i = LBound(arr, 1) To UBound(arr, 1)
Debug.Print VarType(arr(i))
For j = LBound(arr(i), 1) To UBound(arr(i), 1)
'Do Stuff with the arrays
Next
Next
End Sub
This prints
8200
8195
An array always has the VarType 8192 + the value of type. For example String has the Value of 8, therefore an array of Type String has 8200 (8192+8).

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.

VBA Variant Index out of range

I am new to VBA and I need to expand an existing Worksheet and keep its formatting. There are 7 sections with variable length (in rows) and a width of 14 columns that need to be completed. So what I am trying to do is the following:
find the lines where the sections start
select the data out of each section and save it into an array (i thought about this as an array of length 7 and each entry contains a 2-dim. array with the data in it)
select my new data and expand the existing array (created in the last step) with that new data
override the current sheet with my new created array
add formatting
I managed to do step 1 and am currently struggling at step 2: I need to create an array with variable length where I can insert the data.
My code so far:
' this should create the array with the 7 entries
' "myArray" contains the row-numbers where the sections start
Function GenerateSheetArray(sheet As Worksheet, myArray As Variant) As Variant
Dim finalArray As Variant
Dim myInt As Integer
'here each entry should be filled
For i = 0 To 6
myInt = myArray(i)
finalArray(i) = GenerateArrayPart(sheet, myInt)
Next
GenerateSheetArray = finalArray
End Function
'This should fill each entry with the data of corresponding section
Function GenerateArrayPart(sheet As Worksheet, headline As Integer) As Variant
Dim leftIndex As Integer, rightIndex As Integer, rowcount As Integer
Dim sheetArray() As Variant
rowcount = 0
leftIndex = 1
rightIndex = 14
i = headline + 1
Do While sheet.Cells(i, 1) <> ""
rowcount = rowcount + 1
i = i + 1
Loop
If (rowcount > 0) Then
For colIndex = leftIndex To rightIndex
For rowIndex = 1 To rowcount
Row = headline + rowIndex
sheetArray(rowIndex - 1, colIndex - 1) = sheet.Cells(Row, colIndex)
Next
Next
End If
GenerateArrayPart = sheetArray
End Function
Now my problem is, that VBA throws an error at this line:
'atm rowIndex and colIndex are 1, Row is 40
'I know that there is data in that cell
sheetArray(rowIndex - 1, colIndex - 1) = sheet.Cells(Row, colIndex)
VBA says:
Index out of range
in method GenerateArrayPart.
How can this happen? I thought that variant can be pretty much everything and also does not need bounds to be used?
You are not having any value in the array. Thus, the array is only declared and not dimensionized.
Try this:
Dim finalArray As Variant
Redim finalArray(6)
Now, the array would have 7 values inside. From 0 to 6. The same error happens in the Function GenerateArrayPart, with the array sheetArray. There you need to declare the array as a multidimensional array. E.g. Redim sheetArray (N, M).
To see some small working sample, take a look at the code below:
Sub TestMe()
Dim finalArr As Variant
ReDim finalArr(6)
Dim i As Long
For i = LBound(finalArr) To UBound(finalArr)
finalArr = GenerateArrPart(i)
Next i
For i = LBound(finalArr) To UBound(finalArr)
Debug.Print i; finalArr(i, i)
Next i
End Sub
Public Function GenerateArrPart(a As Long) As Variant
Dim i As Long
Dim arrReturn As Variant
ReDim arrReturn(a + 1, a + 1)
For i = LBound(arrReturn) To UBound(arrReturn)
arrReturn(i, i) = a * i
Next i
GenerateArrPart = arrReturn
End Function
This is the output:
0 0
1 6
2 12
3 18
4 24
5 30
6 36
7 42

Resources