2

Say we have some long formula saved in cell 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"))
)

which uses the following VBA function

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

returning a 2×1 array.


Now when I call this VBA function

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

while having selected cell A1 I get an error, because Application.Evaluate cannot handle formulas with more than 255 characters (e.g. see VBA - Error when using Application.Evaluate on Long Formula). On the other hand, if I write

vOutput = Application.Evaluate(Selection.Address)

instead (as proposed in the link above), then it works just fine. Except for the fact that the array is not being recgonised anymore, i.e. MsgBox "Single Value: " is called instead of MsgBox "Array:".

So my question is: How can I evaluate long formulas (which return arrays) using VBA?


Edit: Let me stress that I need this to work when I only select the one cell that conains the formula (not a region or several cells). And I have not entered it as an array formula (i.e. no curly brackets): enter image description here


Edit2: Let me answer the why: my current work requires me to have a long list of such large formulas in a spreadsheet. And since they are organised in a list every such formula can only take up one cell. In almost all cases the formulas return single values (and hence one cell is sufficient to store/display the output). However, when there is an internal error in evaluating the formula, the formula returns an error message. These error messages are usually quite long and are therefore returned as an array of varying size (depending on how long the error message is). So my goal was to write a VBA function that would first obtain and then output the full error message for a given selected entry from the list.

Community
  • 1
  • 1
Phil-ZXX
  • 2,359
  • 2
  • 28
  • 40
  • Thanks for the why. Now this is getting interesting. My first thought after reading your description is: can you enter the array formulas filling into the columns next to the current column ("list")? You can call `TRANSPOSE` on the formula to get columns into rows. It's a little "dirty" but you could just make them all 10 cells wide if that is large enough to capture all the error outputs. I really don't think you're going to get an array output without something like an array for the formula. The question will be how best to do that. – Byron Wall May 19 '15 at 13:23
  • Are your error messages really over 32k in length (or are you trying to get around the in-cell display limit)? In any case, it seems like you could use a "scratch" range to place your formulas and then call Evaluate on them. – Tim Williams May 19 '15 at 16:40
  • @Byron: I also thought of transposing it, but often I have several lists next to each other, which renders this approach impossible. – Phil-ZXX May 19 '15 at 17:59
  • @TimWilliams: The messages are not gigantic, but the formulas simply return arrays if the length of the error string exceeds a certain threshold (and I cannot change these formulas such that they would return longer strings as opposed to arrays). – Phil-ZXX May 19 '15 at 18:01
  • One idea that I had was to perhaps create an empty virtual worksheet object in VBA and copy the formula in there. Then I could potentially perform the approaches that are discussed below. But I am not experienced enough to tell whether this is a viable method. – Phil-ZXX May 19 '15 at 18:04
  • Worksheets cannot be "virtual" - they have to exist. You could create a temporary worksheet, but it's not clear how you can copy your formulas over without then breaking if they contain relative references to other ranges such as `SUM(D3:D6)` in your example. – Tim Williams May 19 '15 at 18:21
  • @TimWilliams I added an edit to my answer which includes a way to use cut/paste to create a new sheet and evaluate it on a new sheet. – Byron Wall May 19 '15 at 20:58

2 Answers2

2

I believe that Application.Evaluate will return a result that matches the size of the input address. I suspect that your Selection is a single cell so it is returning a single value.

If instead you call it with Selection.CurrentArray.Address you will get an answer that is the same size as the correct array.

Picture of VBA and Excel

enter image description here

Code to test with

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

Edit, based on comments here is a way evaluate this off sheet by creating a new sheet. I am using a cut/paste approach to ensure the formulas all work the same. This probably works better if cells don't reference the cut one. It will technically not break any other cells though since I am using cut/paste.

In the code below, I had an array formula in cell J2 it referenced several other cells. It is expanded to have 3 rows and then the Evaluate call is made. That returns an array like you want. It then shrinks it down to one cell and moves it back.

I have tested this for a simple example. I have no idea if it works for the application you have in mind.

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
Byron Wall
  • 3,970
  • 2
  • 13
  • 29
  • I like your version better - @Tom - you should accept this one. – Tim Williams May 19 '15 at 00:20
  • I think this might be the one that breaks the camel's back. Excel was good with an array formula, more than 255 characters, via Eval but maybe not the single cell. I will echo @TimWilliams: why? It seems very circuitous to use a cell to store an array formula that evaluates larger than its container and then access that object in VBA through the cell via Evaluate to get more information than Excel is storing. Is there is a reason you don't just do the formula in VBA since the cell seems to have no bearing on the solution except to hold the formula? What are we missing? – Byron Wall May 19 '15 at 01:07
  • @TimWilliams and Byron: I have added a why to my question. – Phil-ZXX May 19 '15 at 07:59
1

Try

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

(assuming you have two cells with =SomeArrayFunction(...) entered as an array formula)

I think the difference may be that evaluating a single cell will only get you the values returned to that cell: the whole array isn't returned there, only the first value.

Tim Williams
  • 154,628
  • 8
  • 97
  • 125
  • I was too busy lining up Excel, the VBA editor and that Watch window to get the answer in time. I agree with the idea that it is only evaluating the single cell. That's interesting behavior too. – Byron Wall May 19 '15 at 00:17
  • Maybe it might help to explain *why* it needs to work that way. – Tim Williams May 19 '15 at 00:41