【问题标题】:VBA - Long Array Formula via Application.EvaluateVBA - 通过 Application.Evaluate 的长数组公式
【发布时间】:2015-05-18 23:48:45
【问题描述】:

假设我们在单元格 A1 中保存了一些长公式:

=SomeArrayFunction(
IF(SUM(D3:D6)>1,"A-B-C-D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X 01",
"part_one"),
IF(SUM(D3:D6)>1,"A-B-C-D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X 02",
IF(SUM(D3:D6)>1,"A-B-C-D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X 03",
"part_two"))
)

它使用以下 VBA 函数

Public Function SomeArrayFunction(sOne As String, sTwo As String) As Variant
    Dim V() As Variant
    ReDim V(1 To 2, 1 To 1)
    V(1, 1) = sOne
    V(2, 1) = sTwo
    SomeArrayFunction = V
End Function

返回一个 2×1 数组。


现在当我调用这个 VBA 函数时

Public Sub EvaluateFormula()
    Dim vOutput As Variant

    vOutput = Application.Evaluate(Selection.Formula)

    If VarType(vOutput) >= vbArray Then
        MsgBox "Array:" & vbCrLf & vOutput(1, 1) & vbCrLf & vOutput(2, 1)
    Else
        MsgBox "Single Value: " & vbCrLf & vOutput
    End If
End Sub

选择单元格 A1 时出现错误,因为 Application.Evaluate 无法处理超过 255 个字符的公式(例如,请参阅 VBA - Error when using Application.Evaluate on Long Formula)。另一方面,如果我写

vOutput = Application.Evaluate(Selection.Address)

相反(如上面链接中所建议的那样),它就可以正常工作。除了不再计算数组这一事实,即调用 MsgBox "Single Value:" 而不是 MsgBox "Array:"

所以我的问题是:如何使用 VBA 评估长公式(返回数组)?


编辑:让我强调一下,当我只选择包含公式的一个单元格(不是一个区域或多个单元格)时,我需要它来工作。而且我没有将它作为数组公式输入(即没有大括号):


Edit2:让我回答一下原因:我目前的工作要求我在电子表格中列出一长串如此庞大的公式。而且由于它们被组织在一个列表中,每个这样的公式只能占用一个单元格。在几乎所有情况下,公式都返回单个值(因此一个单元格足以存储/显示输出)。但是,当计算公式时出现内部错误时,公式会返回错误消息。这些错误消息通常很长,因此以不同大小的数组形式返回(取决于错误消息的长度)。所以我的目标是编写一个 VBA 函数,该函数将首先获取并输出列表中给定选定条目的完整错误消息。

【问题讨论】:

  • 谢谢你的原因。现在这变得有趣了。阅读您的描述后,我的第一个想法是:您可以输入填充到当前列(“列表”)旁边的列的数组公式吗?您可以在公式上调用TRANSPOSE 以将列转换为行。这有点“脏”,但如果足够大以捕获所有错误输出,您可以将它们全部设为 10 个单元格。我真的不认为你会得到一个没有像公式数组这样的数组输出。问题将是如何最好地做到这一点。
  • 您的错误消息的长度是否真的超过 32k(或者您是否试图绕过单元内显示限制)?无论如何,您似乎可以使用“临时”范围来放置公式,然后对它们调用 Evaluate。
  • @Byron:我也想过转置它,但我经常有几个相邻的列表,这使得这种方法变得不可能。
  • @TimWilliams:消息并不大,但如果错误字符串的长度超过某个阈值,公式只会返回数组(我无法更改这些公式,以便它们返回更长的字符串而不是到数组)。
  • 我的一个想法是在 VBA 中创建一个空的虚拟工作表对象并将公式复制到其中。然后我可能会执行下面讨论的方法。但是我没有足够的经验来判断这是否是一种可行的方法。

标签: arrays vba excel excel-formula


【解决方案1】:

我相信Application.Evaluate 会返回一个与输入地址大小相匹配的结果。我怀疑你的 Selection 是一个单元格,所以它返回一个值。

如果您改为使用 Selection.CurrentArray.Address 调用它,您将得到与正确数组大小相同的答案。

VBA 和 Excel 的图片

用于测试的代码

Public Function Test() As Variant

    Test = Array(1, 2)


End Function

Sub t()

    Dim a As Variant

    a = Application.Evaluate(Selection.CurrentArray.Address)

End Sub

编辑,这里基于 cmets 是一种通过创建新工作表来评估此表外的方法。我正在使用剪切/粘贴方法来确保所有公式都相同。如果单元格不引用剪切的单元格,这可能会更好。但从技术上讲,它不会破坏任何其他单元格,因为我使用的是剪切/粘贴。

在下面的代码中,我在单元格J2 中有一个数组公式,它引用了其他几个单元格。它扩展为 3 行,然后进行 Evaluate 调用。这会返回一个你想要的数组。然后将其缩小为一个单元格并将其移回。

我已经测试了一个简单的例子。我不知道它是否适用于您心目中的应用程序。

Sub EvaluateArrayFormulaOnNewSheet()

    'cut cell with formula
    Dim str_address As String
    Dim rng_start As Range
    Set rng_start = Sheet1.Range("J2")
    str_address = rng_start.Address

    rng_start.Cut

    'create new sheet
    Dim sht As Worksheet
    Set sht = Worksheets.Add

    'paste cell onto sheet
    Dim rng_arr As Range
    Set rng_arr = sht.Range("A1")
    sht.Paste rng_arr

    'expand array formula size.. resize to whatever size is needed
    rng_arr.Resize(3).FormulaArray = rng_arr.FormulaArray

    'get your result
    Dim v_arr As Variant
    v_arr = Application.Evaluate(rng_arr.CurrentArray.Address)

    ''''do something with your result here... it is an array


    'shrink the formula back to one cell
    Dim str_formula As String
    str_formula = rng_arr.FormulaArray

    rng_arr.CurrentArray.ClearContents
    rng_arr.FormulaArray = str_formula

    'cut and paste back to original spot
    rng_arr.Cut

    Sheet1.Paste Sheet1.Range(str_address)

    Application.DisplayAlerts = False
    sht.Delete
    Application.DisplayAlerts = True

End Sub

【讨论】:

  • 我更喜欢你的版本 - @Tom - 你应该接受这个。
  • 我想这可能是压垮骆驼的那个。 Excel 对数组公式很好,超过 255 个字符,通过 Eval 但可能不是单个单元格。我会回应@TimWilliams:为什么?使用单元格存储一个计算值大于其容器的数组公式,然后通过单元格通过 Evaluate 访问 VBA 中的该对象以获取比 Excel 存储的更多信息,这似乎非常迂回。您是否有理由不只是在 VBA 中执行公式,因为除了保存公式之外,单元格似乎与解决方案无关?我们缺少什么?
  • @TimWilliams 和 Byron:我在我的问题中添加了一个原因。
【解决方案2】:

试试

 vOutput = Application.Evaluate(Selection.CurrentArray.Address)

(假设您有两个单元格,=SomeArrayFunction(...) 作为数组公式输入)

我认为不同之处可能在于评估单个单元格只会得到返回到该单元格的值:整个数组不会在那里返回,只有第一个值。

【讨论】:

  • 我忙于排列 Excel、VBA 编辑器和 Watch 窗口,无法及时得到答案。我同意它只评估单个细胞的想法。这也是一种有趣的行为。
  • 也许它可能有助于解释为什么它需要以这种方式工作。
猜你喜欢
  • 1970-01-01
  • 2015-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多