【问题标题】:Return an Excel Array Constant from VBA User-Defined Function从 VBA 用户定义函数返回 Excel 数组常量
【发布时间】:2019-05-16 15:36:49
【问题描述】:

我有一个这样的电子表格:

A1: APPLE
A2: BANANA
A3: ORANGE
A4: APPLE
A5: BANANA
A6: ORANGE

(一直重复到 A20)

所以这个公式会计算“APPLE”的所有实例:

=COUNTIF(A1:A20, "APPLE")

而且这个公式的作用相同,只是范围不连续:

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

INDIRECT 函数采用array constant,范围定义为字符串。请注意花括号和每个范围周围的引号。 (这是基于here 描述的技术。)

但是,如果我定义一个返回字符串数组并将 that 传递给 INDIRECT 的 VBA 函数,似乎只考虑第一个范围。

这是我的 VBA 函数:

Function TestFn() As Variant
    TestFn = Array("A1:A6", "A8:A20")
End Function

这是我的公式:

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

如果我使用 Excel 的“评估公式”按钮,我可以看到 TestFn() 解析为单个字符串“B1:B6”。但是,如果我使用 VBA 调试器,我会看到两个字符串都在其中。

Expression      Value       Type
TestFn                      Variant/Variant(0 to 1)
 - TestFn(0)    "B1:B6"     Variant/String
 - TestFn(1)    "B8:B20"    Variant/String

如何从 Excel VBA 函数返回一个字符串数组,该函数可以以相同的方式传递到 INDIRECT

我认为这归结为:在没有 VBA 的情况下构造公式时使用花括号创建了哪些内部对象,我可以从 VBA 中创建相同的内部对象吗?


我也尝试过返回字符串数组,而不是包含字符串数组的变体。

Function TestFn() As String()
    TestFn = Split("B1:B6,B8:B20", ",")
End Function

这些是调试器中的类型:

Expression      Value       Type
TestFn                      String(0 to 1)
 - TestFn(0)    "B1:B6"     String
 - TestFn(1)    "B8:B20"    String

但结果是一样的。我想我需要使用某种容器类型,但我不确定该使用什么或如何弄清楚该使用什么。


如果有更好的方法,我实际上不需要使用SUM...INDIRECT hack,如果合适的话,我很乐意使用数组公式。

如果我可以让我的函数直接返回范围,而不是表示范围的字符串,那将比我目前的方法更好。

如果我可以设置一个类似这样的数组公式(不必完全像这样):

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

然后让我的 UDF 看起来像这样:

Function TestFn() As Range()
    TestFn = Array(Range("A1:A6"), Range("A8:A20"))
End Function

或者可能是这样的:

Function TestFn() As Range
    TestFn = Union(Range("A1:A6"), Range("A8:A20"))
End Function

这将满足我的需要。

这里的关键是我只希望我的 VBA 只定义哪些单元格被操作,而不是操作是什么,我想处理非连续范围。我希望在 excel 公式中定义实际操作,因为这些操作将被不希望了解如何读写 VBA 代码的人修改。 (哎呀,我也不想再看 VBA 代码了。)在这种情况下,我使用的是 COUNTIF,但我的 VBA 函数也将与其他 excel 公式函数一起使用。

【问题讨论】:

  • 如果有人想知道“为什么?”:我的真实数据在过滤表中。 VBA 函数将一个范围作为输入并返回该范围的当前可见的子集(即未过滤掉)。我想使用尽可能少的 VBA 并将大部分逻辑保留在 excel 中。我将使用 SUM...INDIRECT 技巧和几个不同的 excel 函数,而不仅仅是 COUNTIF,所以简单地制作 COUNTIF 的“变体”不是很可持续。
  • 您是否使用 Ctrl+Shift+Enter 作为数组公式输入了 =SUM(COUNTIF(INDIRECT(TestFn()), "APPLE"))?因为这适用于我的原始版本 TestFn
  • 不同之处在于您需要将其作为数组公式输入到 excel 工作表中(它将获取两个范围)和 vba,如果您要在 vba 中使用不带花括号的 .evaluate 这个相同的公式它被自动识别为数组公式。
  • @BigBen 我没有,就像普通公式一样。我不知道存在数组公式。我将阅读数组公式,看看它们是否符合我的目的。但是,我仍然很好奇为什么非 VBA 公式可以像普通公式一样工作,以及我是否可以用 VBA 复制它。
  • @JvdV 我正在努力理解您所描述的内容,但恐怕它已经超出了我的想象。

标签: excel vba excel-formula


【解决方案1】:

当然有人可以更好地解释这一点,但让我试试。你都用过:

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

还有……

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

关于第一个公式;您基本上已经使用 INDIRECT() 函数将文本字符串转换为实际范围。实际上,INDIRECT() 的用法非常好,我必须说:)。

但是使用你告诉 Excel 它不仅仅是一个字符串的大括号这样做,你已经为函数提供了多个数据,一个数组!通常,Excel 中的数组是一组简单的数据。此数据可以是文本、数字或两者兼有。您使用了文本。

在第二个公式中,您通过 UDF 为公式提供了一个数组,但它缺少大括号。 Excel 不知道您要比较多个范围,只会计算数组中的第一项。

但是使用 CtrlShiftEnter 你告诉 Excel 你想用一个数组提供公式,作为一组数据/范围比较。

所以:

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

会工作:)

我相信其他人会更好地解释;)

【讨论】:

  • 有趣。我仍在试图弄清楚数组公式是如何工作的。使用数组公式是否可以完全放弃SUM...INDIRECT hack?或者至少删除INDIRECT?
  • 也许如果我返回一个实际范围的数组而不是表示范围的字符串数组?反正这样会干净得多……
  • @WoodrowBarlow,好吧。你的目标是什么?你需要喂配方奶吗?我的意思是,虽然你已经有了一个 UDF,为什么不在那里做数学呢?
  • 我不需要公式,数组公式就可以了。我正在使用 UDF 获取当前可见的单元格范围子集(它们来自过滤表)。在这种情况下,我将该范围子集与COUNTIF 一起使用,但我也希望将它与其他功能一起使用。将使用此电子表格(并添加更多公式)的人不希望编辑 VBA 代码,所以我不想创建 COUNTIF 等的变体,我只想让他们成为能够根据当前可见的范围子集编写公式。
  • 如果他们需要编写数组公式而不是普通公式,那很好。
猜你喜欢
  • 2015-08-12
  • 1970-01-01
  • 1970-01-01
  • 2013-08-13
  • 1970-01-01
  • 2022-06-20
  • 2017-03-04
  • 2013-01-24
  • 1970-01-01
相关资源
最近更新 更多