简体   繁体   中英

Return an Excel Array Constant from VBA User-Defined Function

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.

Surely someone can explain this better, but let me try. You have used both:

=SUM(COUNTIF(INDIRECT({"A1:A6","A8:A20"}), "APPLE"))

And...

=SUM(COUNTIF(INDIRECT(TestFn()), "APPLE"))

About the first formula; you have basically used the INDIRECT() functions to translate text strings into a real range. It's actually a very nice use of INDIRECT() I must say :).

But doing so using the curly brackets you told Excel that it is more than just one string, you have fed the function with multiple data, an array! Typically , an array in Excel is a simple set of data. This data can be text, numbers, or both. You have used text.

In the second formula you have fed the formula with an array through an UDF but it's lacking the curly brackets. Excel doesn't know you want to compare multiple ranges and will only evaluate the first item in your array.

However using Ctrl Shift Enter you telling Excel you want to feed the formula with an array, being a set of data/ranges to compare.

So:

{=SUM(COUNTIF(INDIRECT(TestFn()), "APPLE"))}

Will work :)

I'm sure someone else is better in explaining ;)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM