Using multiple occurrences of the same custom function in Excel - 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.

Related

Why doe my Excel VBA function return a #VALUE error when the debug print is correct?

Sorry if this question seems a bit basic by I am only a amateur. I tried to write a small VBA function to filter the bottom values from a range on a sheet and then average the remaining values. When I ran this through a subroutine it printed off the correct value, however, when I used it as a function (with the exact same input values) it returns a #VALUE error on my worksheet.
I recently found that the same thing can be accomplished by using a small function and an if statement in excels default average function. But its really bugging me why this didn't work. Would anybody have any suggestions?
Below is my Code:
Function TOPSCORES(SheetArray As Variant, Length As Integer) As Variant
'Commented code are the additions in the sub routing variant that print the correct value
'Dim SheetArray() As Variant
'Dim Length As Integer
'SheetArray = Range("B3:B14")
'Length = 10
Dim SortVal As Variant
Dim i As Integer
Dim j As Integer
Dim Final As Variant
'Filter top scores
For i = 1 To UBound(SheetArray)
For j = i + 1 To UBound(SheetArray)
If SheetArray(j, 1) > SheetArray(i, 1) Then
SortVal = SheetArray(i, 1)
SheetArray(i, 1) = SheetArray(j, 1)
SheetArray(j, 1) = SortVal
End If
Next j
Next i
'Average top scores
For i = 1 To Length
Final = Final + (SheetArray(i, 1))
Next i
Final = Final / Length
'Debug.Print (Final)
TOPSCORES = Final
End Function
A Variant/Object/Range is not the same as an array of Variant.
Your SheetArray As Variant coming from a user defined function, which gets called =TOPSCORES(B3:B14,10), is a Variant/Object/Range. The code line SheetArray = Range("B3:B14") in a Sub results in an array of Variant. That is because no Set keyword is used and so SheetArray cannot be an object. So SheetArray = Range("B3:B14") defaults to use SheetArray = Range("B3:B14").Value.
To make your function working with a Variant/Object/Range coming from the user defined function it should be like so:
Function TOPSCORES(SheetValues As Variant, Length As Integer) As Variant
Dim SheetArray As Variant
SheetArray = SheetValues
...
That works if SheetValues comes as Variant/Object/Range because SheetArray = SheetValues then implicit defaults to SheetArray = SheetValues.Value since no Set keyword. And it also works if SheetValues is an array already since it then simply sets SheetArray = SheetValues.

VBA "Variable required. Can't assign to this expression" error - Goal-seek analysis

I'm a beginner with VBA and by no means a programmer. I am trying to write a macro which iterates through cells in a row and carries out goal-seek analysis on multiple cells. I could not use any built-in functions from Excel because goal-seek analysis is only possible for one cell and not many.
My macro should iterate through a specific range of cells in a row (from columns A to E) while carrying out goal-seek analysis. I could not get this to work without a loop or through using something like For Each Row In Range so I tried creating an array of letters for the second loop to work.
However, when I run the program I get a "Variable required. Can't assign to this expression" error. Could someone point out what I'm doing wrong or if I could do something more efficiently?
Thank you
Sub CommandButton1_Click()
Dim c As String
c = "ABCDE"
'Create array of letters to iterate through cells in a row
Dim i, j As Long
Dim c_array() As String
ReDim c_array(Len(c) - 1)
For j = 1 To Len(c)
c_array(j - 1) = Mid$(c, j, 1)
Next
'Goal seek analysis
For i = 0 To (Len(c_array) - 1)
Cells(3, c_array(i)).GoalSeek Goal:=Cells(2, "G"), ChangingCell:=Cells(2, c_array(i))
Next i
End Sub
Dim i, j As Long does only declare j As Long but i As Variant. In VBA you need to specify a type for every variable:
Dim i As Long, j As Long
The issue you get is Len(c_array) where it is defined as array Dim c_array() As String. Len does not work with arrays. If you want to know the upper or lower bound of the array you need to use LBound and Ubound:
For i = LBound(c_array) To UBound(c_array)
But actually there is no need to put the data into an array. You can use the string directly:
Const c As String = "ABCD"
Dim i As Long
For i = 1 To Len(c)
Dim Col As String
Col = Mid$(c, i, 1)
Cells(3, Col).GoalSeek Goal:=Cells(2, "G"), ChangingCell:=Cells(2, Col)
Next i
Or even better use column numbers instead of their letters if they are consecutive.
ABCD is column 1 to 4:
Dim iCol As Long
For iCol = 1 To 4
Cells(3, iCol).GoalSeek Goal:=Cells(2, "G"), ChangingCell:=Cells(2, iCol)
Next iCol

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

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).

#VALUE! Error when trying to call a UDF function into another UDF function

Simply put, what I am attempting to do is a simple function that would calculate the sample variance of a selected range of cells in excel. The effect is to get a result exactly identical to the excel function =VAR.S()
I have coded in the user-defined function for mean (see below), but the error arises when trying to call my Mean1b function into my Var2a function. If I had simply defined mean as a constant in my variance formula, everything goes smoothly.
My question is, why am I not simply able to pass the input array through my Var2a function, such that it would be accepted by Mean1b function? What would also be the workaround this if I were to continue working in ParamArray?
Public Function Mean1b(ParamArray arr() As Variant)
Dim rtot As Double
Dim elem As Variant
Dim i As Integer
Dim count As Integer
rtot = 0 'set initial state
count = 0 'initiate count of elements in the array
For i = LBound(arr) To UBound(arr) 'loop from lower bound to upper bound of array
For Each elem In arr(i)
rtot = rtot + elem.Value
count = count + 1
Next elem
Next i
Mean1b = rtot / count
End Function
Public Function Var2a(ParamArray arr() As Variant) As Double
Dim rtot As Double
Dim elem As Variant
Dim i As Integer
Dim count As Integer
mean = Mean1b(arr)
rtot = 0
count = 0
For i = LBound(arr) To UBound(arr)
For Each elem In arr(i)
rtot = rtot + (elem.Value - mean) ^ 2
count = count + 1
Next elem
Next i
Var2a = rtot / (count - 1)
End Function
Solved it! Turns out all you had to do was to modify this:
mean = Mean1b(arr)
to this:
mean = Mean1b(arr(0))
Apparently this is required when passing a ParamArray into another function

Resources