Excel Offset performance gain bij adding if()? Answer; NO - excel

update
The volatile functions can't be mitigated with the IF() statement. See answers below. 1
I've just heard my excel sheets need to run on potato speed laptops...
Would;
=IF(A1="Test"** ; OFFSET(B5 ; MATCH(C8 ; G10:G15) ; OFFSET(B5 ; MATCH(C8 ; G9:F9)) ; 0)
be quicker than just the offset functions
=OFFSET(B5 ; MATCH(C8 ; G10:G15) ; OFFSET(B5 ; MATCH(C8 ; G9:F9)) ; 0)
My gut says yes... But I can't find a clear answer if the "unused" part of the IF() function isn't calculated or "volatile".
I have around 120 offset functions on the sheet with 200 MATCH functions. Depending on a lot of variables between 50 and 90 offsets are actually used for the result at one time.
I hope to hear from you,
Koen.
PS; it wouldn't make a difference if I would switch the function and the zero right?
PS; it would be a temporal fix til I can put stuff into the (quicker?) index function.
update
The volatile functions can't be mitigated with the IF() statement.

Last I read, trying to short circuit a Volatile Function with IF still results in the Volatile function being executed, even if it is in the FALSE part of the statement. So what you want to do is remove the volatile OFFSET function rather than try to short circuit it. The INDEX function can return a dynamic range, so that's what I'd use. See my answer at Can Excel's INDEX function return array?
Here's Charles Williams on the matter:
Using a volatile function in a formula will flag the cell containing the formula as volatile, even if the volatile function never gets executed:
=IF(1<2,99,NOW()) will always return 99 and the volatile NOW() function will never be called, but the cell containing the IF formula
will be treated as volatile, (thanks to Stephen Bullen for pointing
this out).
If cell A1 contains =NOW() then =IF(1<2,99,A1) will always return 99,
but the cell containing the IF formula will NOT be treated as
volatile.

Related

Getting "Too many functions" in Excel formula

I am writing a formula for Excel. Excel says that I entered too many arguments. Am I missing any brackets or my formula is wrong?
10000+IF(AND(F5>=98%, (D17-D125)>0),((D5*2.5)+(0.2*(D17-D125))),(D5*2.5),IF(AND(98%>F5,F5>=90%,(D17-D125)>0),((D5*1.5)+(0.1*(D17-D125))),(D5*1.5),IF(AND(90%>F5,F5>=80%,(D17-D125)>0),((D5*0.5)+(0.05*(D17-D125))),(D5*0.5),IF(AND(F5<80%),(D17-D125)>0),((D5*0)+(0*(D17-D125))),(D5*0))))
Is there any shorter formula to use?
So, quick example to show how vlookup() works, Excel has good examples in the Function Reference. vlookup() can have all the arguments built-in so only one cell needed. Just shown it like this to show.
=VLOOKUP(A2,$A$7:$B$11,2,1)
I don't know what your intentions are mathematically, and it definitely seems like theres a simple solution here. But when analyzing your formula.. I had trouble almost instantly when breaking it up into a more procedural If/then/else structure.. So you do have misplaced parens, extra parameters to IF(), etc.
I really like the LET() function to setup variable names or simply to avoid repeating myself. And then since you're chaining IFs, just use IFS() instead..
LET() works like LET(name1, value1, name2, value2, result).
IFS() works like IFS(condition1, resultIf1, condition2, resultIf2)
Something like the following is at least easier on the eyes..
LET(
VarA, F5,
VarB, D17-D125,
VarC, D5,
VarD, VarB > 0,
IFS(
AND(VarA > 98%, VarD), D5*2.5,
AND(VarA < 98%, VarA >= 90%, VarD), D5*1.5 + 0.1*VarB,
AND(VarA < 90%, VarA >= 80%, VarD), D5*0.5 + 0.5*VarB,
...
TRUE, default value
)
)

Add Together a Set of Numbers using LAMBDA Recursion in Excel

I have a continuous set of numbers in cell I32 (123456789) of my worksheet. I want to use the new LAMBDA function to add each number with nine iterations (using recursion). The final value in cell L32 should be 45.
What I've got at the moment produces a #VALUE! error. I am basing it off the =REPLACECHARS example shown at the Microsoft Excel blog, where invalid characters are stripped out of a cell.
Formula:
=LAMBDA(Number,NumberGroup,
AddNumbers(
SUM(LEFT(Number,1)),
RIGHT(NumberGroup,LEN(NumberGroup)-1)
)
)(L32)
You are not using any recursion currently. You just trying to sum. If you really want recursion inside your LAMBDA() try something like:
=LAMBDA(Input,AddAll,X,IF(X=LEN(Input)+1,AddAll,Addnumbers(Input,AddAll+MID(Input,X,1),X+1)))
I created three parameters for LAMBDA():
Input - A reference to your cell/string of numbers;
AddAll - A grand total of the added numbers;
X - A simple counter, as if we were writing a function in VBA;
If you add the function to your name manager you can now call this using: =Addnumbers(I32,0,1), meaning:
Call our LAMBDA() function which we named "AddNumbers";
The 1st parameter must be the reference to our cell/string;
The 2nd parameter is our current total, zero at the start;
The 3rd parameter is the start of our counter, which should be 1.
I specifically added the nested IF() to get iteration going, i.o.w. recursion. The IF() checks the current state of the counter. Only if that is bigger than the total length of our input it will return the current total, otherwise; the recursion starts with calling the LAMBDA() all over again in the 2nd parameter (the FALSE parameter) where we use:
The same Input value;
Use MID() to add one of the numbers from the current index to our total of AddAll;
Increase our counter X by 1.
Now we have recursion out of the way, I'd say you have better options here if you want to use LAMBDA() since recursion is not needed. Try:
=LAMBDA(Input,SUM(--MID(Input,SEQUENCE(LEN(Input)),1)))
Call through =Addnumbers(I32).

Excel VBA Function Fails but Formula works in sheet

Weird Math Error in VBA for Excel
Hi all, would love feedback on unsusual error I'm getting.
Very strange. I have a simple formula that works great if I
only use it in a normal sheet cell and copy it down by columns,
but if I try to do a simple iteration in vba code to perform the same function I get the wrong values.
Description : A number is squared, then divided by another value
between 0.99 to 1.99, next the modulus is taken and then
the number is squared again and the whole formula repeated.
If I copy the formula statement down column wise it calcs fine,
including reasonable decimal accuracy.
There are four inputs ;
base value (inputx)
decx = divisor
mod value
The first formula placed at (E2) looks like ; =MOD(((B2^2)/$B$3),$B$4)
In (E3) this statement is placed ; =MOD(((E2^2)/$B$3),$B$4)
Then this exact same statement is copied down, columnwise to the next 98 cells.
All great, no problem. It seems accurate value wise, right to decimal
precision, with values past the decimal point showing in all column cells.
Some sample input values for testing ;
INPUTX --> 231
DECX 1.010101
MOD 400
LOOPTIMES 100
But when I try to implement this is Excel VBA code (Excel 2007)
I often get the wrong values and absolutely no values past the decimal point ever show.
Have tried using all kinds of different data types ; single, double, variant, etc... but all values returned by the VBA function I made always returns
whole numbers, and is often wrong and certainly does not agree with the
values returned by the simple column based statements.
Have tried to find ways around this or to fix this, came across "CDEC", tried
this and nothing changed. Totally stumped and would love some insight into
if this can be fixed so that the function loop returns the same values with
same kind of decimal precision as the column based statements and
would greatly appreciate feedback on how mthis can be done.
Am including my sample code below ;
Public Function SQRD(inputx As Variant, looptime As Variant, decx As Variant) As Variant
Application.Volatile
Dim Count As Integer
SQRD = CDec(inputx)
'Dim decx As variant
Count = 1
For Count = 1 To looptime
SQRD = CDec(SQRD ^ 2) '+ looptime
SQRD = CDec(SQRD Mod 400 / decx)
Next Count
End Function
I will only address your use of the VBA Mod operator. It is NOT equivalent to the Excel MOD function. In particular, the VBA Mod operator will round floating point numbers to integers before performing the operation.
To use a VBA function that mimics the Excel MOD function, use something like:
Function xlMOD(a As Double, b As Double) As Double
xlMOD = a - (b * (a \ b))
End Function
EDIT
There seems to be a bug in VBA (or a documentation error). In the formula above, the \ operator is supposed to be the integer division operator. It does return an integer result. However, it does not truncate, rather it rounds. Actually, what it may be doing, is performing VBA type rounding on the number and divisor, before returning the result.
Therefore, a proper vba function to mimic the Excel MOD function would be:
Function xlMOD(a As Double, b As Double) As Double
xlMOD = a - Int(a / b) * b
End Function
Lots amiss with your code. No need for looping as far as I can see, and you're dividing after the mod not before
This seems to do the trick
Public Function NuFunc(InputX As Variant, DecX As Variant) As Variant
NuFunc = ((InputX ^ 2) / DecX) Mod 400
End Function

Return all text to the right of last numeric digit in strings

Return the right text string of a cell until the first number from the right e.g.
"geb. 14 oct 1956 Westerkerk HRL" must return Westerkerk HRL
"geb. 14 oct 1956" must return empty cell
" " must return empty cell
Formula solution:
=TRIM(MID(A1,1+MAX(FIND({1,2,3,4,5,6,7,8,9,0},A1&"1234567890")*ISNUMBER(FIND({1,2,3,4,5,6,7,8,9,0},A1))),LEN(A1)))
EDIT: I realized that the above formula may run into issues if there is a repeated number (for example if it had been 1966 instead of 1956), so I went back and came up with this which will not be affected by that issue:
=TRIM(MID(A1,1+LOOKUP(2,1/ISNUMBER(--MID(A1,ROW(INDIRECT("1:"&LEN(A1))),1)),ROW(INDIRECT("1:"&LEN(A1)))),LEN(A1)))
However, INDIRECT is a volatile function and I usually try to avoid it. You could instead use a number which is going to be higher than the length of your text strings in order to accomplish the same goal without a volatile function, though this may not actually be any faster than using the INDIRECT function in the formula:
=TRIM(MID(A1,1+LOOKUP(2,1/ISNUMBER(--MID(A1,ROW($1:$255),1)),ROW($1:$255)),LEN(A1)))
So basically, you can use either formula, whichever you prefer.
A bit of monkey work - someone might have a simpler solution - but first you'd need to get the highest lastIndexOf '0' through '9'. In your example, the number is '6'. From that, you can create a substring.
var c = 'geb. 14 oct 1956 Westerkerk HRL'; // c for companyname
var subc = '';
var startsub = c.lastIndexOf('6') + 2; // Point past the space to the W.
if(c.length > startsub) subc = c.substring(startsub, c.length);
return subc;

Case Function Equivalent in Excel

I have an interesting challenge - I need to run a check on the following data in Excel:
| A - B - C - D |
|------|------|------|------|
| 36 | 0 | 0 | x |
| 0 | 600 | 700 | x |
|___________________________|
You'll have to excuse my wonderfully bad ASCII art. So I need the D column (x) to run a check against the adjacent cells, then convert the values if necessary. Here's the criteria:
If column B is greater than 0, everything works great and I can get coffee. If it doesn't meet that requirement, then I need to convert A1 according to a table - for example, 32 = 1420 and place into D. Unfortunately, there is no relationship between A and what it needs to convert to, so creating a calculation is out of the question.
A case or switch statement would be perfect in this scenario, but I don't think it is a native function in Excel. I also think it would be kind of crazy to chain a bunch of =IF() statements together, which I did about four times before deciding it was a bad idea (story of my life).
Sounds like a job for VLOOKUP!
You can put your 32 -> 1420 type mappings in a couple of columns somewhere, then use the VLOOKUP function to perform the lookup.
Without reference to the original problem (which I suspect is long since solved), I very recently discovered a neat trick that makes the Choose function work exactly like a select case statement without any need to modify data. There's only one catch: only one of your choose conditions can be true at any one time.
The syntax is as follows:
CHOOSE(
(1 * (CONDITION_1)) + (2 * (CONDITION_2)) + ... + (N * (CONDITION_N)),
RESULT_1, RESULT_2, ... , RESULT_N
)
On the assumption that only one of the conditions 1 to N will be true, everything else is 0, meaning the numeric value will correspond to the appropriate result.
If you are not 100% certain that all conditions are mutually exclusive, you might prefer something like:
CHOOSE(
(1 * TEST1) + (2 * TEST2) + (4 * TEST3) + (8 * TEST4) ... (2^N * TESTN)
OUT1, OUT2, , OUT3, , , , OUT4 , , <LOTS OF COMMAS> , OUT5
)
That said, if Excel has an upper limit on the number of arguments a function can take, you'd hit it pretty quickly.
Honestly, can't believe it's taken me years to work it out, but I haven't seen it before, so figured I'd leave it here to help others.
EDIT: Per comment below from #aTrusty:
Silly numbers of commas can be eliminated (and as a result, the choose statement would work for up to 254 cases) by using a formula of the following form:
CHOOSE(
1 + LOG(1 + (2*TEST1) + (4*TEST2) + (8*TEST3) + (16*TEST4),2),
OTHERWISE, RESULT1, RESULT2, RESULT3, RESULT4
)
Note the second argument to the LOG clause, which puts it in base 2 and makes the whole thing work.
Edit: Per David's answer, there's now an actual switch statement if you're lucky enough to be working on office 2016. Aside from difficulty in reading, this also means you get the efficiency of switch, not just the behaviour!
The Switch function is now available, in Excel 2016 / Office 365
SWITCH(expression, value1, result1, [default or value2, result2],…[default or value3, result3])
example:
=SWITCH(A1,0,"FALSE",-1,"TRUE","Maybe")
Microsoft -Office Support
Note: MS has updated that page to only document the behavior of Excel 2019. Eventually, they will probably remove references to 2019 as well... To see what the page looked like in 2016, use the wayback machine:
https://web.archive.org/web/20161010180642/https://support.office.com/en-us/article/SWITCH-function-47ab33c0-28ce-4530-8a45-d532ec4aa25e
Try this;
=IF(B1>=0, B1, OFFSET($X$1, MATCH(B1, $X:$X, Z) - 1, Y)
WHERE
X = The columns you are indexing into
Y = The number of columns to the left (-Y) or right (Y) of the indexed column to get the value you are looking for
Z = 0 if exact-match (if you want to handle errors)
I used this solution to convert single letter color codes into their descriptions:
=CHOOSE(FIND(H5,"GYR"),"Good","OK","Bad")
You basically look up the element you're trying to decode in the array, then use CHOOSE() to pick the associated item. It's a little more compact than building a table for VLOOKUP().
I know it a little late to answer but I think this short video will help you a lot.
http://www.xlninja.com/2012/07/25/excel-choose-function-explained/
Essentially it is using the choose function. He explains it very well in the video so I'll let do it instead of typing 20 pages.
Another video of his explains how to use data validation to populate a drop down which you can select from a limited range.
http://www.xlninja.com/2012/08/13/excel-data-validation-using-dependent-lists/
You could combine the two and use the value in the drop down as your index to the choose function. While he did not show how to combine them, I'm sure you could figure it out as his videos are good. If you have trouble, let me know and I'll update my answer to show you.
I understand that this is a response to an old post-
I like the If() function combined with Index()/Match():
=IF(B2>0,"x",INDEX($H$2:$I$9,MATCH(A2,$H$2:$H$9,0),2))
The if function compare what is in column b and if it is greater than 0, it returns x, if not it uses the array (table of information) identified by the Index() function and selected by Match() to return the value that a corresponds to.
The Index array has the absolute location set $H$2:$I$9 (the dollar signs) so that the place it points to will not change as the formula is copied. The row with the value that you want returned is identified by the Match() function. Match() has the added value of not needing a sorted list to look through that Vlookup() requires. Match() can find the value with a value: 1 less than, 0 exact, -1 greater than. I put a zero in after the absolute Match() array $H$2:$H$9 to find the exact match. For the column that value of the Index() array that one would like returned is entered. I entered a 2 because in my array the return value was in the second column. Below my index array looked like this:
32 1420
36 1650
40 1790
44 1860
55 2010
The value in your 'a' column to search for in the list is in the first column in my example and the corresponding value that is to be return is to the right. The look up/reference table can be on any tab in the work book - or even in another file. -Book2 is the file name, and Sheet2 is the 'other tab' name.
=IF(B2>0,"x",INDEX([Book2]Sheet2!$A$1:$B$8,MATCH(A2,[Book2]Sheet2!$A$1:$A$8,0),2))
If you do not want x return when the value of b is greater than zero delete the x for a 'blank'/null equivalent or maybe put a 0 - not sure what you would want there.
Below is beginning of the function with the x deleted.
=IF(B2>0,"",INDEX...
If you don't have a SWITCH statement in your Excel version (pre-Excel-2016), here's a VBA implementation for it:
Public Function SWITCH(ParamArray args() As Variant) As Variant
Dim i As Integer
Dim val As Variant
Dim tmp As Variant
If ((UBound(args) - LBound(args)) = 0) Or (((UBound(args) - LBound(args)) Mod 2 = 0)) Then
Error 450 'Invalid arguments
Else
val = args(LBound(args))
i = LBound(args) + 1
tmp = args(UBound(args))
While (i < UBound(args))
If val = args(i) Then
tmp = args(i + 1)
End If
i = i + 2
Wend
End If
SWITCH = tmp
End Function
It works exactly like expected, a drop-in replacement for example for Google Spreadsheet's SWITCH function.
Syntax:
=SWITCH(selector; [keyN; valueN;] ... defaultvalue)
where
selector is any expression that is compared to keys
key1, key2, ... are expressions that are compared to the selector
value1, value2, ... are values that are selected if the selector equals to the corresponding key (only)
defaultvalue is used if no key matches the selector
Examples:
=SWITCH("a";"?") returns "?"
=SWITCH("a";"a";"1";"?") returns "1"
=SWITCH("x";"a";"1";"?") returns "?"
=SWITCH("b";"a";"1";"b";TRUE;"?") returns TRUE
=SWITCH(7;7;1;7;2;0) returns 2
=SWITCH("a";"a";"1") returns #VALUE!
To use it, open your Excel, go to Develpment tools tab, click Visual Basic, rightclick on ThisWorkbook, choose Insert, then Module, finally copy the code into the editor. You have to save as a macro-friendly Excel workbook (xlsm).
Even if old, this seems to be a popular questions, so I'll post another solution, which I think is very elegant:
http://fiveminutelessons.com/learn-microsoft-excel/using-multiple-if-statements-excel
It's elegant because it uses just the IF function. Basically, it boils down to this:
if(condition, choose/use a value from the table, if(condition, choose/use another value from the table...
And so on
Works beautifully, even better than HLOOKUP or VLOOOKUP
but... Be warned - there is a limit to the number of nested if statements excel can handle.
Microsoft replace SWITCH, IFS and IFVALUES with CHOOSE only function.
=CHOOSE($L$1,"index_1","Index_2","Index_3")
Recently I unfortunately had to work with Excel 2010 again for a while and I missed the SWITCH function a lot. I came up with the following to try to minimize my pain:
=CHOOSE(SUM((A1={"a";"b";"c"})*ROW(INDIRECT(1&":"&3))),1,2,3)
CTRL+SHIFT+ENTER
where A1 is where your condition lies (it could be a formula, whatever). The good thing is that we just have to provide the condition once (just like SWITCH) and the cases (in this example: a,b,c) and results (in this example: 1,2,3) are ordered, which makes it easy to reason about.
Here is how it works:
Cond={"c1";"c2";...;"cn"} returns a N-vector of TRUE or FALSE (with behaves like 1s and 0s)
ROW(INDIRECT(1&":"&n)) returns a N-vector of ordered numbers: 1;2;3;...;n
The multiplication of both vectors will return lots of zeros and a number (position) where the condition was matched
SUM just transforms this vector with zeros and a position into just a single number, which CHOOSE then can use
If you want to add another condition, just remember to increment the last number inside INDIRECT
If you want an ELSE case, just wrap it inside an IFERROR formula
The formula will not behave properly if you provide the same condition more than once, but I guess nobody would want to do that anyway
If your using Office 2016 or later, or Office 365, there is a new function that acts similarly to a CASE function called IFS. Here's the description of the function from Microsoft's documentation:
The IFS function checks whether one or more conditions are met, and returns a value that corresponds to the first TRUE condition. IFS can take the place of multiple nested IF statements, and is much easier to read with multiple conditions.
An example of usage follows:
=IFS(A2>89,"A",A2>79,"B",A2>69,"C",A2>59,"D",TRUE,"F")
You can even specify a default result:
To specify a default result, enter TRUE for your final logical_test argument. If none of the other conditions are met, the corresponding value will be returned.
The default result feature is included in the example shown above.
You can read more about it on Microsoft's Support Documentation

Resources