I have a spreadsheet like this:
A1: APPLE
A2: BANANA
A3: ORANGE
A4: APPLE
A5: BANANA
A6: ORANGE
(This repeats until A20)
So this formula would count all instances of "APPLE":
=COUNTIF(A1:A20, "APPLE")
And This formula does the same, except with a non-contiguous range:
=SUM(COUNTIF(INDIRECT({"A1:A6", "A8:A20"}), "APPLE"))
The INDIRECT function is taking an array constant with the ranges defined as strings. Note the curly braces and the quotes around each range. (That's based on the technique described here.)
However, if I define a VBA function that returns an array of strings and pass that into INDIRECT, it seems that only the first range is respected.
This is my VBA function:
Function TestFn() As Variant
TestFn = Array("A1:A6", "A8:A20")
End Function
This is my formula:
=SUM(COUNTIF(INDIRECT(TestFn()), "APPLE"))
If I use Excel's "Evaluate Formula" button, I can see that TestFn() resolves to a single string "B1:B6". However, if I use the VBA debugger, I see that both strings are in there.
Expression Value Type
TestFn Variant/Variant(0 to 1)
- TestFn(0) "B1:B6" Variant/String
- TestFn(1) "B8:B20" Variant/String
How can I return an array of strings from an Excel VBA function that can be passed into INDIRECT in the same manner?
I think this comes down to: what internal object is created by using curly braces when constructing the formula without VBA, and can I create that same internal object from within VBA?
I've also tried returning an array of strings, rather than a variant containing a string array.
Function TestFn() As String()
TestFn = Split("B1:B6,B8:B20", ",")
End Function
These are the types in the debugger:
Expression Value Type
TestFn String(0 to 1)
- TestFn(0) "B1:B6" String
- TestFn(1) "B8:B20" String
But the result is the same. I think I need to use some type of container type, but I'm not sure what to use or how to figure out what to use.
I don't actually need to use the SUM...INDIRECT hack if there is a better way, and I'd be happy to use an array formula if that's appropriate.
If I could let my function return ranges directly, rather than strings representing ranges, that would be even better than my current approach.
If I could set up an array formula something like this (doesn't have to be exactly like this):
{=COUNTIF(TestFn(), "APPLE")}
Then let my UDF look something like this:
Function TestFn() As Range()
TestFn = Array(Range("A1:A6"), Range("A8:A20"))
End Function
Or maybe something like this:
Function TestFn() As Range
TestFn = Union(Range("A1:A6"), Range("A8:A20"))
End Function
That would suit my needs.
The key here is that I just want my VBA to only define which cells are operated on, not what the operation is, and i want to deal with non-contiguous ranges. I want the actual operations to be defined in the excel formulas, because these will be modified by people who aren't expected to understand how to read and write VBA code. (Heck, I don't want to ever look at VBA code again either.) In this case, I'm using COUNTIF, but my VBA function will be used with other excel formula functions as well.
=SUM(COUNTIF(INDIRECT(TestFn()), "APPLE"))as an array formula with Ctrl+Shift+Enter? Because that works for me with your original version ofTestFn..evaluatethis same formula without curly brackets in vba it is automatically recognized as being an array formula.