Excel UDF that accepts both Range and Array as Parameter like 'SUM' - arrays

I an writing a UDF that needs to accept both Arrays and Ranges.
Usually declaring parameter as variant would work but a Range is an object so this no longer applies. That being said bellow I pasted code that only works when passing an array.
Here is theorethical example, based on SUM:
Function TSUM(numbers() As Variant) As Variant
Dim i As Integer
For i = 1 To UBound(numbers, 1)
TSUM = TSUM + numbers(i)
Next i
End Function
=TSUM({1,1}) Returns 2
=TSUM(A1:B1) Returns #VALUE!
So how can I fix above example to accept Ranges as well?

If you are content to sum the array/range item by item, I would just change to using a For Each loop that works well for either Ranges or Arrays.
Here is that version
Public Function TSUM(numbers As Variant) As Variant
Dim i As Variant
For Each i In numbers
TSUM = TSUM + i
Next i
End Function
If you generally want to work a function based on the type of the argument, you can use TypeName() and switching logic. Here is you function with that approach. I called it TSUM2 for uniqueness.
Public Function TSUM2(numbers As Variant) As Variant
Dim i As Integer
If TypeName(numbers) = "Range" Then
TSUM2 = Application.WorksheetFunction.Sum(numbers)
Else
For i = 1 To UBound(numbers, 1)
TSUM2 = TSUM2 + numbers(i)
Next i
End If
End Function
Note in both examples, I removed the parentheses from the numbers parameters (was numbers() as Variant before). This allows it to accept Range inputs.
If you take the second approach, be sure to debug and verify the TypeNames that could come through.

Related

accessing individual array elements in VBA function

VBA newbie here. I am trying to pass an array (it is static, but please answer for dynamic range as well) to a function. Then assign individual array elements to unique variables and use these variables in a custom formula. I just browsed around and wrote the code but keep getting #VALUE! error. The gist of the code is below:
Public Function mytest(ByRef arr1 As Range)
Dim A As Double
Dim B As Double
A = arr1(0)
B = arr1(1)
mytest = A + B 'The actual formula is a bit more complicated than simple addition
End Function
I am not sure what am i doing wrong at all. If anyone has a solution, can you please explain why it works as well. I appreciate any and all help I can get.
Many thanks !
As Coleman pointed out a range is not an array, consider:
Public Function mytest(ByRef arr1 As Range)
Dim A As Double
Dim B As Double
A = arr1(1, 1)
B = arr1(2, 1)
mytest = A + B 'The actual formula is a bit more complicated than simple addition
End Function
NOTE:
we treat the Range similar to an array
it is two dimensional
it is 1 based
if you are only dealing with the Range's value, you could create an internal array within your function that directly maps to the passed Range.
if the Range is truly dynamic, (like a Spill range) then all you need to pass is the anchor cell.
You seem to be trying to use a worksheet range as a 0-based array. That doesn't really make sense although using the Cells property of a range (which you are actually trying to do implicitly) you can come close:
Public Function mytest(arr1 As Range)
Dim A As Double
Dim B As Double
A = arr1.Cells(1)
B = arr1.Cells(2)
mytest = A + B 'The actual formula is a bit more complicated than simple addition
End Function
In the above code, you can drop Cells() since it will function as the default property here, but most experienced VBA programmers like to make explicit what property they are using.
This will more or less work for 1-dimensional ranges but might not work as expected with 2-dimensional ranges. Cells takes up to 2 indices and in general I think that the code is clearer when you are explicit about the full indices (e.g. A = arr1.Cells(1,1) and B = arr1.Cells(2,1)).
The question isn't in the code you posted but in the procedure that calls it. Here the calling procedure first assigns values to the cells in the worksheet (for testing purposes), then passes the range to the function which extracts the values into an array and then uses that array to calculate a return value.
Private Sub TestmyTest()
Dim Rng1 As Range
Cells(1, "A").Value = 3.14
Cells(2, "A").Value = 3
Set Rng1 = Range("A1:A2")
Debug.Print myTest(Rng1)
End Sub
Function myTest(Rng1 As Range) As Double
' procedures are 'Public' unless declared as 'Private'
' Therefore only declare "Private" or nothing
' Arguments are passed ByRef unless they are declared as 'ByVal'
' Therefore I recommend to omit "ByRef"
Dim Arr As Variant
Dim A As Double
Dim B As Double
' this creates a 1-based 3-D array of 2 row and 1 column
Arr = Rng1.Value
A = Arr(1, 1)
B = Arr(2, 1)
myTest = A + B 'The actual formula is a bit more complicated than simple addition
End Function

How to pass a dynamcally allocated array into a function in Excel VBA

I am very fresh with Excel VBA, so I ask you for understanding. Doing research on the internet has not resolved my problem. So I wish to pass an array of numerical values in VBA into a function, namely enter cash flows into an NPV function all in Excel VBA code.
Dim Postavke() As String, velikost As Integer, i As Integer
velikost = WorksheetFunction.CountA(Worksheets("List1").Columns(11))
ReDim Postavke(velikost)
For i = 1 To velikost
Postavke(i) = Cells(i + 1, 11).Value
Next i
Now I want to pass the Postavke() array to the NPV function:
Dim NSV As Long
NSV = NPV(0.06, ByRef Postavke() As Long))
which always goes an error. Any thoughts to how this might be done?
Thank you in advance. Best regards.
Create the function in this way:
Function NPV(whatever As Double, Postavke() As String) As Long
Dim x As Long
'do whatever you want with your code, work on the 'x' variable and finally ends with:
NPV = x
'or define NPV earier and use Exit Function after..
End Function
You must decide if the Postavke() array must be declared as a string or a long. I kept your initial declaration...
And call the function like this:
NSV = NPV(0.06, Postavke())
In order to properly load velikost, starting with 1, you must add on top of your module code:
Option Base 1
Otherwise, you must take care (like #SJR remarked) that the array is zero-based by default.

Is there a way to create a VBA function that can take in both arrays and ranges as input?

I am trying to create a function that can take in both a range or an array to perform some further calculations. When an array passes, the function worked fine, but when the function is used on range in the worksheet, it gives me the VALUE! error.
My code looks like:
Function COMRET(data as variant, N as integer)
Dim nrows as long
If IsArray(data) Then
N = UBound(data,1)
Else
N = data.rows.count
End If
'... some other calculations here
End Function
The problem seems to come from the identification of an array above... other parts of the code seems OK when I comment out the IF section above. Not sure what I am doing wrong here. Appreciate the help. Thanks!
You are correct to declare data as Variant.
When you pass a range into that, it will be correctly passed as Variant/Object/Range, but then things will get a little bit more complicated.
When you call IsArray, which is a mere function, it will try to use the default property of the passed Range object, which is Value. IsArray(Range.Value) is True provided that the Range consist of more than one cell. But when you then call UBound, which is a very special function because it is highlighted as a keyword, it will not try to fetch data.Value and will operate on data directly, and data is a single Range object, so it cannot have an upper bound (while its .Value can).
You need to properly detect what you are being passed:
Public Function COMRET(data As Variant, N As Integer)
Dim nrows As Long
If TypeOf data Is Range Then
N = data.Rows.Count
ElseIf IsArray(data) Then
N = UBound(data, 1)
Else
' ???
End If
End Function
You're passing data as a Variant, which will cause IsArray(data) to always come out as true, regardless of whether you pass a range or array to it when calling the function.
Adding a third compulsory argument would work too, indicating the data type of the data variable:
Function COMRET(data As Variant, Is_Array As Boolean, N As Integer)
Dim nrows As Long
If Is_Array = True Then
N = UBound(data, 1)
Else
N = data.Rows.Count
End If
'... some other calculations here
End Function
Alternatively, you could add a check that uses the error thrown by attempting to use UBound on a range. This clears the need of a third argument:
Function COMRET(data As Variant, N As Integer)
Dim nrows As Long
On Error Resume Next
N = UBound(data, 1)
If Err.Number <> 0 Then N = data.Rows.Count
Err.Clear
On Error GoTo 0
'... some other calculations here
End Function
VarType() should be your friend:
' Const vbArray = 8192 (&H2000)
' Const vbString = 8
Debug.Print VarType(data) ' = vbArray (8192) Or vbString (8) = 8200
' Is data an array of sorts?
Debug.Print CBool((VarType(data) And vbArray) = vbArray)
From MSDN help:
The VarType function never returns the value for vbArray by itself. It
is always added to some other value to indicate an array of a
particular type. The constant vbVariant is only returned in
conjunction with vbArray to indicate that the argument to the VarType
function is an array of type Variant. For example, the value returned
for an array of integers is calculated as vbInteger + vbArray, or
8194. If an object has a default property, VarType (object) returns the type of the object's default property.

Passing String of arrays through a function in VBA

I am creating several string arrays and attempting to use a function for each array on an excel sheet. It is supposed to go through each row and every row it counts to see if any of the strings match with the value in the currently active cell. I seem to get and error when i try to pass the string array to the function and get an empty value for the function parameter. Here is my code
the array
anArray = Array("string1", "string2", "string3")
the function
Function checkArray(a as Variant) as integer
Range("A1")
Dim count As Integer
count = a.Length - 1
Do While ActiveCell.Value <> ""
Do While count <> -1
If ActiveCell.Value = a(count) Then
checkArray = checkArray + 1
End If
count = count -1
Next i
ActiveCell.Offset(1, 0).Select
Loop
End Function
and i call it
checkArray(anArray)
There seems to be a few necessary things missing from your sample function code.
Functions cannot select cells to process them but you can pass a range of one or more cells into the function as a parameter to be processed.
You've described your array using VBA code but made no mention as to how the function is supposed to determine the nature of the array beyond being fed it as an incoming parameter. This conflicts with teh nature of the remainder of the sample function code since it looks like it is to be used as a UDf worksheet function.
Here is what I would expect to work as a worksheet UDF function.
Function checkArray(rng As Range, Optional a As Variant) As Long
Dim v As Long, vSTRs As Variant
If IsMissing(a) Then
vSTRs = Array("string1", "string2", "string3")
Else
vSTRs = a
End If
For v = LBound(vSTRs) To UBound(vSTRs)
checkArray = checkArray + Application.CountIf(rng, vSTRs(v))
Next v
End Function
The optional a variant array parameter can be passed in as a constant array or it is defined within the function by the default values stored within the function.
Syntax: =checkArray(<range of cells to check>, <optional array of strings>)
Examples: =checkArray(A1:A10)
                  =checkArray(A1:A10, {"abc", "def", "JKL"})
      
This function can also be called from within a Sub to assign a long integer value to a declared variable.
Sub test()
Dim num As Long
num = checkArray(ActiveSheet.Range("A1:A10"), Array("string1", "string2", "string3"))
Debug.Print num
End Sub

Assigning an array value to a variable in VBA causes my UDF to exit

I can't seem to figure out why this UDF is exiting on the currentInput = inputArray(i). Here is the relevant code:
Function OrderRange(inputRange As Range) As Variant
Dim length As Integer
inputHeight = inputRange.Count
Dim inputArray As Variant
inputArray = inputRange
Dim strippedArray() As Variant
ReDim strippedArray(0 To (inputHeight - 1))
Dim currentInput As String
Dim i As Integer
For i = 0 To (inputHeight - 1)
currentInput = inputArray(i)
'...computations on currentInput...'
strippedArray(i) = currentInput
Next i
OrderRange = strippedArray
End Function
The debugger reaches currentInput = inputArray(i) but once I move to the next line, the function terminates and a #VALUE! error is entered into the cell I call the function from. I know this is a specific question, but I'm sure it's a general issue and I'll edit this original post to reflect what the general problem is.
Edit: This is an issue regarding assignment of a range to a variant array.
Variant arrays created by setting them equal to ranges have two dimensions even if they are only one column, or row, wide. So if you call the function with A1:A10 you'll get a 10 * 1 array. Also the lower bound of the dimensions will be one, not zero. So you'd have to do something like:
For i = 1 To (inputHeight)
currentInput = inputArray(i, 1)
Also you should use Option Explicit so that you're reminded to declare all variables. InputHeight is never declared.

Resources