VBA loop preference - excel

I am currently trying to optimize a VBA code with extreme execution time (56 hours and more on high end PC). The main problem is nested loops (up to 8) due to complicated iterative calculations and referencing ranges in formulas.
I have two questions:
1)
What loops are most preferred in VBA (based on their hardware strain for lack of better word)
2)
Is there a way to reference variable range of cells in a formula? For example in formula:
=AGGREGATE(15,6,SQRT((R2C10:R500000C10-RC[10])^2+(R2C11:R500000C11-RC[11])^2+(R2C12:R500000C12-RC[12])^2),1)
I am referencing relative cell: "R500000C10". There are two main issues: For one, the half milion cells might not capture all data - though in most cases there is less active cells than that, in same cases that might be an issue. Second, it takes forever to scan through so many cells (same reference is used in multiple of VLOOKUPs).
The first issue could be solved by referencing the whole column, but that makes the second problem worst.
I would like to reference it in a similar way that xlDown works:
Range().End(xlDown).Select
The problem there is I would need to reference it within the Active.Cell.FormulaR1C1. Is there a way to do that?
Thanks!

Well you wrote that you have a code with 8 loops and try to optimize it maybe you can edit and put part of that code, because your description is very general, however, with information that you put i speculate that about your point
1)it depend what do you want to do? because each "loop" have their own use for specifyc scenaries
2)there are tricks to do that but all depend what are you trying to do and why you want to use .formulaR1C1
again im speculating but you can try tu separate your loops in indenpendent function, and maybe you are putting a formula inside cells, and question is why? you can create functoins and Sub in your VBA code and do your "extrem execution" without use xtra "resours"

Related

Evaluate long string (>255 characters) as array

So I quite often find myself doing tasks on Excel which involve evaluating a text string as an array. Generally speaking I just use this:
Function EVAL(Ref As String)
EVAL = Evaluate(Ref)
End Function
So the formula will be, for example:
=EVAL("{"&CHAR(34)&SUBSTITUTE(TEXTJOIN(";",TRUE,MID(Index[Industries],2,LEN(Index[Industries])-2)),";",CHAR(34)&";"&CHAR(34))&CHAR(34)&"}")
The cells in this example will have contents like:
;Automotive;Rail;Energy;
;Automotive;Rail;
;Energy;
;Automotive;Aerospace;
(As it happens this is the precise problem I'm stuck on right now, though it has come up in different ways in the past.)
This has worked for me in the past, but I've been running into difficulties lately.
I have come to the conclusion it isn't working because application.evaluate, it turns out, has a character limit of 255. I've seen examples of VBA tricks to bypass this for text strings that are formulas rather than arrays, but copy-pasting those they don't seem to work for when I'm using it to interpret a text string as an array rather than as a formula.
Is there some trick to get this to work? (Or, indeed, is there some alternative method to achieve this altogether?)
Right, as per my comments, if you are using ms365, you could avoid your workbook to be xlsm just because you need to split values into an array. Make use of what is available with native functions, for example:
Formula in C2:
=TEXTSPLIT(CONCAT(A1:A4),,";",1)
Formula in D2:
=FILTERXML("<t><s>"&SUBSTITUTE(CONCAT(A1:A4),";","</s><s>")&"</s></t>","//s[node()]")
Note 1: As per time of writing you'd need to enable the BETA-channel to gain access to TEXTSPLIT(), and if I recall correctly your version (2203) is allowed to start using this function. Just google how to get access and update your Excel.
Both options can obviously be nested inside the UNIQUE() function.
Note 2: If at any point CONCAT()'s limits are reached (32767 characters, thanks #ScottCraner), maybe you can avoid using that with help of the lambda's helper function REDUCE():
=TEXTSPLIT(REDUCE("",A1:A4,LAMBDA(a,b,a&b)),,";",1)
Note 3: In case you can't update your Excel just yet, and you wonder how to use FILTERXML(), don't mind me refering you to another post I wrote a while back here.

EXCEL - Circular Reference Error when Entering Data

I've got another one that is really kicking my butt.
I know why the error is occurring. I just don't know how to fix it. There are a lot of IF statements so I'm thinking maybe they are arranged in a way that is causing the error. I'm sure there is a much cleaner way to write them.
Whenever I try to input data into D7 or E7 I get the circular reference error.
These are my current formulas for all relevant cells:
F7: =IF(C7<0,"FAIL",IF(OR(ISBLANK(B7),ISBLANK(C7))," ",IF(ABS(F8)<=30,"PASS","FAIL")))
G7: =IF(D7<0,"FAIL",IF(OR(ISBLANK(B7),ISBLANK(D7))," ",IF(ABS(G8)<=30,"PASS","FAIL")))
H7: =IF(E7<0,"FAIL",IF(OR(ISBLANK(B7),ISBLANK(E7))," ",IF(ABS(H8)<=30,"PASS","FAIL")))
F8: =IF(B7<0,"",IF(C7<0,"",IF(D7<0,"",IF(E7<0,"",IF(G7="FAIL","",IF(H7="FAIL","",IF(ISBLANK(B7),IF(ISBLANK(C7),"","input Lw_Lw"),IF(ISBLANK(C7),"input Lw_Up",SUM(C7-B7)))))))))
G8: =IF(B7<0,"",IF(C7<0,"",IF(D7<0,"",IF(E7<0,"",IF(F7="FAIL","",IF(H7="FAIL","",IF(ISBLANK(B7),IF(ISBLANK(D7),"","input Lw_Lw"),IF(ISBLANK(D7),"input Up_Lw",SUM(D7-B7)))))))))
H8: =IF(B7<0,"",IF(C7<0,"",IF(D7<0,"",IF(E7<0,"",IF(G7="FAIL","",IF(F7="FAIL","",IF(ISBLANK(B7),IF(ISBLANK(E7),"","input Lw_Lw"),IF(ISBLANK(E7),"input Up_Up",SUM(E7-B7)))))))))
Snip of Excel table
Any help would be much appreciated!
I worked out the following two formulas for you. Please try them.
[F7] =IF(IFERROR(ABS(F8)<=30,FALSE),"PASS","FAIL")
[F8] ==IF(ISBLANK(C7),"input "&F$3,IF(OR(COUNT($B7:$E7)<4,COUNTIF($B7:$E7,"<0")>0),"",SUM(C7-$B7)))
Copy them from F7:F8 to G7:H8.
The basic principle I applied in order to avoid a circular reference is to do all testing in row 8 with the outcome that F8 will either hold a blank or a number. Therefore "Pass" or "Fail" in row 7 can be decided based on the number: If the number is within range it's a PASS, else it's failed. Note that Abs("") will cause an error. Therefore IFERROR(ABS(F8)<=30,FALSE) will return False in case F8 = "". Effectively, this is the reverse of what I wrote in my comment above.
I looked for shorter expressions for other tests as well. Count() will only count cells with numbers in them. Therefore I use this function instead of a series of ISBLANK() queries. Similarly for COUNTIF($B7:$E7,"<0").
I used mixed absolute and relative addressing to enable copying formulas to other columns and referred to the column captions in place of repeating the same texts in the formulas.
I didn't fully test my solution. However, with my above explanation as a guide you should be able to take possession and eliminate any errors I might have left behind.

Write formula to an entire range, or just loop through cells with an IF statement. Which is faster?

I'm using a macro to re-write formulas to a range in case a cell gets unintentionally modified by a user.
Would looping through each cell and fixing errors be faster then rewriting the entire range? Thoughts?
I have tried protecting sheets and certain ranges, but still occasionally have issues.
Currently using:
Sheet2.Range("TableSh[Produced]").Formula = "=SUMIF(TableP[Run],[#Run],TableP[Good PCS])"
Would this ↓ be faster?
For Each R In Range("TableSh[Produced]")
If Not R.Formula = "=SUMIF(TableP[Run],[#Run],TableP[Good PCS])" Then
R.Formula = "=SUMIF(TableP[Run],[#Run],TableP[Good PCS])"
End If
Next
The question "Would this ↓ be faster?" is obviosly unnecessary if you would just run both of them and stop your time (you could easily answer that yourself).
As Vityata already said, there might only very rare cases where a loop is faster than not using a loop.
For example if the formula would be a heavy load calculation then the loop could benefit in some cases, especially when it only has to write very vew formulas (so we have only a vew heavy load calculations instead of a whole column).
In general I would say always try to avoid looping if there is another solution. But there is no general answer to which one is faster. Better test it on your data if you are in doubt, it highly depends on the exact situation.

Speed up Excel formulas

Is there anyway to rewrite this formula to speed up Excel processing?
My spreadsheet has become terribly slow!
=SUMPRODUCT((Sheet1!J:J=Sheet2!A2)*(Sheet1!G:G="Windows XP")*(Sheet1!B:B="Desktop")*(Sheet1!M:M<>"Refresh >=Q2 2014")*(Sheet1!M:M<>"Release 2013")*(Sheet1!M:M<>"Release 2014")*(Sheet1!M:M<>"N/A NVM")*(Sheet1!M:M="No")*(Sheet1!M:M="N/A"))
As written your formula will always return zero because the last two conditions are mutually exclusive - did you mean those last two to be <> rather than = (or did you refer to the wrong columns)?
In any case I can see from the use of whole columns that you must be using Excel 2007 or later (your current formula would give an error otherwise) in which case COUNTIFS will be much faster, i.e. assuming the last two conditions should be adjusted as I suggested try this version:
=COUNTIFS(Sheet1!J:J,Sheet2!A2,Sheet1!G:G,"Windows XP",Sheet1!B:B,"Desktop",Sheet1!M:M,"<>Refresh >=Q2 2014",Sheet1!M:M,"<>Release 2013",Sheet1!M:M,"<>Release 2014",Sheet1!M:M,"<>N/A NVM",Sheet1!M:M,"<>No",Sheet1!M:M,"<>N/A")
If you do need to use SUMPRODUCT then restrict the ranges rather than using whole columns
I don't think that there is really a chance to speed up Excel Formula. But you could save your File in binary code (.xlsb). Losing some compatibility but improving performance.
You also could stop auto (re-)calculations of ther Formula, then you have to manualy refresh. This will let you edit the file much smoother.

INDIRECT() returns #VALUE! unexpectedly

Background: I'm using Excel functions to parse a lot of data out, essentially creating a flexible pivot table. It sorts a lot of race timing data by car, etc. In this portion of the sheet, I'm searching for the minimum segment times for each car. The rest of the sheet avoids macros and VBA so I'd like to avoid that here.
Issue: My formula worked when there are no zeros, but sometimes there are zeros that I need to exclude. My array formula is pretty complicated, but the change I made that broke it is this:
OLD (working):
{=min(if(car_number = indirect("number_vector"), indirect("data_vector")))}
NEW (non-working):
{=min(if(and(car_number = indirect("number_vector"),not(0=indirect("data_vector"))), indirect("data_vector")))}
I am using INDIRECT() with this exact argument several times in the formula. However, in this particular instance (inside the NOT()), it returns #VALUE! instead of {data1;...;datan}. Please see the screencaps below.
Before evaluation:
After evaluation:
I suspect that your AND function might be a problem - AND only returns a single result not an array of results as required, try using multiple IFs like this
=min(if(car_number = indirect("number_vector"),IF(indirect(data_vector)<>0, indirect(data_vector))))
Note that I also used <> rather than using NOT
Are data vector and number vector the same size and shape? (both vertical?)
why are there quotes around one but not the other?

Resources