Unwanted parentheses are automatically inserted when writing formula to cell - excel

I'm trying to set a formula for a cell in Excel but I have a strange problem. With the line:
Range("BG4").Formula = "=CustomSum(BG:BG)"
it works fine and the correct formula is set into the cell. But I have another line of code:
Range("BH4").Formula =
"=if(R1C1<>"""","""",if(CustomSum(BH:BH)=0,""geplant!"",CustomSum(BH:BH)))"
This line produces a formula in the cell which looks like this:
=IF($A$1<>"";"";IF(CustomSum(BH:(BH))=0;"geplant!";CustomSum(BH:(BH))))
which is unacceptable since it doesn't work (parentheses in parameter are inserted automatically).
How do I avoid that?
CustomSum looks as follows:
Public Function CustomSum(rng As Range)
Dim Sum As Double
Sum = 0
For row = 10 To 48 Step 2
If IsNumeric(Cells(row, rng.Column).Value) Then
Sum = Sum + Cells(row, rng.Column).Value
End If
Next
CustomSum = Sum
End Function

For the sake of an answer, copied from a Comment by #simoco:
"=if(R1C1<>"""","""",if(CustomSum(BH:BH)
... you can't mix R1C1 and normal formulas

I had the same issue, and found a response here: Absolute referencing - Inserting the equivalent of '$' in an equation written in R1C1 notation.
Long story short, since you have R1C1 at the beginning of your formula, the BH:BH needs to be in R1C1 coordinates as well. BH is column 60. I'm not sure how to specify a whole column as you would in BH:BH, but if your total rows will always be under some large number (500? 6000? depending on your data), you could specify a large range and hopefully have it work fine.
If you have a title for your column, and your data starts row 2, I would write it like this:
Range("BH4").Formula =
"=if(R1C1<>"""","""",if(CustomSum(R2C60:R6000C60)=0,""geplant!"",CustomSum(R2C60:R6000C60)))"

Related

How can I substitute a cell reference for the formula the referred cell contains

How can I substitute a cell reference for the formula it contains, in other words, "expand" or "derivate" cell references?
An example, and I know I could calculate it using PV(): Suppose I want to calculate the present value of a given amount, reductor, number of periods and discount rate and in a spreadsheet I have:
A2: 1 (number of periods)
B2: 5000 (amount)
C2: 0,8 (reductor)
G1: 6% (discount rate)
If I want to calculate the final result on D2, I would have to enter:
=(B2*C2)*(1+$G$1)^(-A2)
(I intentionally used some unnecessary parentheses above)
But if I wanted, for debugging, or for building a more complex formula with more nested calculations write on cells:
D2: =E2*F2^G2
E2: =B2*C2
F2: =1+$G$1
G2: =-A2
So that I could check every part of the calculation is working ok and that the final formula is well "assembled" (or to easily correct what might be wrong or change it to calculate something else, like future value, for which I would remove the minus sign on G2).
And after doing those steps use some function/shortcut/feature on cell D2 that would replace
"=E2*F2^G2"
for
"=(B2*C2)*(1+$G$1)^(-A2)"
(i.e. do E2 → (B2*C2) F2 → (1+$G$1) and G2 → (-A2)) so that the desired formula is built on the right place and I can get rid of the temporary cells.
The closest to this behaviour I could find was formulatext() function, but it works just for a single reference and always include the "=" if I do, for instance
=CONCAT(FORMULATEXT(E2);"*";FORMULATEXT(F2);"^";FORMULATEXT(G2))
results in
=B2*C2*=1+$G$1^=-A2
which is not the desired result.
What I was expecting to find was something like when one select a part of a formula and presses F9 and it substitutes it for the value, but applied for functions or intermediate steps.
As it really does not seem to exist a built-in funcion on Excel, I came out with a script for doing this based on the answer on Parsing and extracting cell references from Excel formulas?
Works on Excel 365 (may work on other versions as well), replaces references on active cell only, does not work on cells that contain intervals (for instance, it will fail on a cell that contains =sum(A1:A5) ) and the contents of the precedent cells will end up enclosed in parentheses. It also does not replace "locked" cells (=$B$2 won't be replaced as well).
In summa, it is not perfect, maybe it's not ellegant too, but it seems to be as good as I needed and works on the proposed scope.
Sub ReplacePrecedents()
Dim r As Range, rr As Range
With ActiveCell.Range("A1")
' store the contents of the cell
parsedcontents = .Formula
Set r = .DirectPrecedents
' iterate throughout all precedents
For Each rr In r
' store each one between parentheses
appendstr = "("
' check whether first character is a "=" or a value
If StrComp(Left(rr.Range("A1").Formula, 1), "=") = 0 Then
appendstr = appendstr & Right(rr.Range("A1").Formula, Len(rr.Range("A1").Formula) - 1)
Else
appendstr = appendstr & rr.Range("A1").Formula
End If
appendstr = appendstr & ")"
' do the magic
parsedcontents = Replace(parsedcontents, rr.Address(0, 0), appendstr)
Next rr
' write the parsed string to the cell
.Formula = parsedcontents
End With
End Sub
Thank you for everyone that replied, I guess I still do not have privileges enough to upvote a comment, as soon as I do, I will.

Formula autofill manipulation

I am trying to create a large spreadsheet(10,000 rows of formulas) that takes information in from two other sheets. The basic layout that I want is:
Row1
=Sheet1!A7
=Sheet2!M7
=Sheet1!A8
=Sheet2!M8
=Sheet1!A9
=Sheet2!M9
...etc.
When I try to use to formula auto fill, excel picks up on the wrong pattern and I end up with something like this:
=Sheet1!A7
=Sheet2!M7
...
=Sheet1!A11
=Sheet2!M11
=Sheet1!A17
=Sheet2!M17
I gave excel 10 cells to base the pattern off of, and have not been able to get it to work. Can anyone help me figure out how to do this (hopefully without VBA)?
Try to avoid the volatile¹ OFFSET function or INDIRECT / ADDRESS function pairs in favor of the INDEX function and a little maths.
In the first two cells use these two formulas.
=INDEX(Sheet1!A:A, 7+ROW(1:1)/2)
=INDEX(Sheet2!M:M, 7+ROW(1:1)/2)
Select the two cells and drag the Fill Handle down.
¹ Volatile functions recalculate whenever anything in the entire workbook changes, not just when something that affects their outcome changes. Examples of volatile functions are INDIRECT, OFFSET, TODAY, NOW, RAND and RANDBETWEEN. Some sub-functions of the CELL and INFO worksheet functions will make them volatile as well.
Here is a simple VBA macro that sets up your links. Use it until a non-VBA solution is presented:
Sub propagator()
Dim i As Long, k As Long
k = 1
For i = 7 To 99
Cells(k, 1).Formula = "=Sheet1!A" & i
k = k + 1
Cells(k, 1).Formula = "=Sheet2!M" & i
k = k + 1
Next i
End Sub
Just select the destination worksheet and run the macro.
Adjust the 99 to meet your needs.
You can solve this without VBA with some =INDIRECT trickery -- the following is located in "Sheet3":
You can type out Sheet1!A and Sheet2!M in cells A1 and A2 respectively and fill down. Then, type a 7 in B1 and the formula in B2 -- again fill down. This first formula is effectively incrementing the count by two. Finally, you can type the formula in C1 and fill down.
Use the INDIRECT function to build this, and you can make the pattern work based on your current row number. Assuming your first cell is in Row 2:
=INDIRECT("Sheet1!A" & 7+(ROUNDDOWN(ROW()/2,0)-1))
=INDIRECT("Sheet2!M" & 7+(ROUNDDOWN(ROW()/2,0)-1))
ROW() returns the current row, which is then divided by 2 (since you only increase one row reference for every two cells), and then subtracted 1 (since we want the first adjustment to be 0).
Try in row 2:
=INDIRECT("Sheet1!A" & 7 +QUOTIENT(ROW()-2,2))
And in row 3:
=INDIRECT("Sheet2!M" & 7 +QUOTIENT(ROW()-2,2))
Highlight both and copy down.

Using MIN/MAX in excel array formula

I have a simple array formula in excel that doesn't work in the way I wish. In columns A and B there is data (A1 is paired with B1 and so on) while in column F there is the calculation based on the parameter in column E.
In cell F1 the formula is:
{=SUM(MAX(A$1:A$9, E1)*B$1:B$9)}
What this formula does is:
=MAX(A$1:A$9, E1)*B$1 + MAX(A$1:A$9, E1)*B$2 + ...
Instead, I need a formula that does this:
=MAX(A$1, E1)*B$1 + MAX(A$2, E1)*B$2 + ...
In words, the formula I wrote (the first one) always finds the max between the values from A1 to A9 and E1, multiplies it by the i-th B value and sums the results. What I need is a formula that finds the max between the i-th A value and E1, and not between all the A values.
What I'm looking for is easily done by adding in column C the formula =MAX(A1;E$1)*B1 and then in F1 just =SUM(A1:A9), but I can't use this solution because in column F the same formula is repeated, with the E parameter changing every time.
I can use a IF instruction: in F1 I can write
{=SUM(IF(A$1:A$9>E1, A$1:A$9, E1)*B$1:B$9)}
While this formula does what I need in this case, I think it's a bad solution because I find it difficult to read and to expand. For example, if there is another parameter in column D and the factor is MIN(MAX(A$1:A$9;E1);D1), using IF will result in a very long and very unreadable and complicated formula.
Are there better solutions to my problem? Thank you all!
NOTE: syntax may vary a little because I am using the italian version of excel.
The problem is that MAX takes an array as an argument. Functions that normally take an array never return an array - they were designed to turn an array into one number. No matter how many arrays you throw at MAX, it's always just going to return one number.
I couldn't come up with a good solution, so here's a bad one
=SUMPRODUCT(((A1:A9*(A1:A9>E1))+(E1*(A1:A9<=E1)))*B1:B9)
I don't think that really increases the maintainability of the IF-based formula that you're trying to avoid. I think you're stuck with IF or a helper column.
Another possibility is a VBA function.
Public Function SumMaxMin(rRng1 As Range, rRng2 As Range, ParamArray vaMinMax() As Variant) As Double
Dim rCell As Range
Dim dReturn As Double
Dim aMult() As Double
Dim lCnt As Long
Dim i As Long
ReDim aMult(1 To rRng1.Cells.Count)
For Each rCell In rRng1.Cells
lCnt = lCnt + 1
aMult(lCnt) = rCell.Value
For i = LBound(vaMinMax) To UBound(vaMinMax) Step 2
If Not Evaluate(aMult(lCnt) & vaMinMax(i + 1) & vaMinMax(i)) Then
aMult(lCnt) = vaMinMax(i)
End If
Next i
Next rCell
For i = LBound(aMult) To UBound(aMult)
dReturn = dReturn + aMult(i) * rRng2.Cells(i).Value
Next i
SumMaxMin = dReturn
End Function
For your example
=SumMaxMin(A1:A9,B1:B9,E1,">")
Adding another condition
=SumMaxMin(A1:A9,B1:B9,E1,">",D1,"<")
It will error if your ranges aren't the same number of cells or you pass arguments that don't work with Evaluate.
Another possibility for avoiding repetitions of cell references is:
=SUM(B1:B9*ABS(A1:A9-E1*{1,-1}))/2
assuming values are non-negative. More generally to return an array of pairwise max values:
=MMULT((A1:A9-E1*{1,-1})^{2,1}^{0.5,1},{1;1}/2)
which returns:
MAX(A1,E1)
MAX(A2,E1)
...
MAX(A9,E1)
I don't remember ever cracking this problem, but for maintainability I'd probably do something like this:
{=SUM((A1:A9<E1)*E1*B$1:B$9) + SUM((A1:A9>=E1)*A1:A9*B$1:B$9)}
If I understand the problem correctly, using IF instead of MAX should do:
=SUM(IF($A$1:$A$9>E1;$A$1:$A$9;E1)*$B$1:$B$9)

Count number of numbers in a single cell

I want to count the number of numbers in a single cell.
Example:
In cell A1, I have the following addition:
=12+45+23+51+10 which totals (and shows) 141.
In cell B1, I would like the see how many numbers have been added together, which means that there should be 5 (12 is a number, 45 another one, etc... and all together, there are 5 numbers in cell A1).
I know it seems to be a ridiculous question, but I scanned all the platforms for this issue and did not find any suitable solution. Tried all the LEN and LEN SUBSTITUTE alternatives out there, but somehow it does not work.
Thank you upfront for your help. Optimal solution would be a excel formula, alternatively VBA would also work.
Excel 2013 has a new function Formulatext() which returns the, well, formula text. Using that the Len() and Substitute() approach works.
=LEN(FORMULATEXT(A1))-LEN(SUBSTITUTE(FORMULATEXT(A1),"+",""))+1
Hit Alt+F11 and Insert->Module with the following VBA:
Function NumCount(Rng As Range)
x = Split(Rng.Formula, "+")
NumCount = UBound(x) + 1
End Function
Then you can call =NumCount(A1) on any cell to get the number of numbers in the formula for that cell.
Use this VBA:
Function GetFormula(Cell as Range) as String
GetFormula = Cell.Formula
End Function
and then to get the number of numbers...
=LEN(GetFormula(D99))-LEN(SUBSTITUTE(GetFormula(D99),"+",""))
on this as my D99 contents...
=45+46+47+50+100
The one major drawback here is that I'm assuming everything is + if you have -, *,/ or other operators this gets more challenging. you might be able to use replace for each but you'd always be limited to the 4 basic operators... if someone used exponents or mod, this would be harder.
Also possible in earlier Excel versions without VBA - subject to (i) there is always at least one value in the cells, (ii) the operator is always + and (iii) the cells are in a column.
Replace = with '= in that column, apply the substitution, say:
=LEN(A1)-LEN(SUBSTITUTE(A1,"+",""))+1
in a different column and Paste Special, Value the results for that other column. Then apply Text To Columns on the original column with ' as the delimiter.
*There is no way to do this without using a User Defined Function (UDF) written in Excel VBA. Something like this should work:
Public Function numsAdded(cell1 As Range)
Dim formulaString As String
formulaString = cell1.Formula
numsAdded = Len(formulaString) - Len(Replace(formulaString, "+", "")) + 1
End Function
Once you've added that to a VBA module, you can use it as a function in any cell in your spreadsheet.
*Edit - Apparently there is a way if you have Excel 2013, as teylyn suggests. If you use 2010 or earlier like me, you'll need VBA.
Try this:
=LEN(SUBSTITUTE(F16,"+","+"))
Note: F16 is only an example name for the cell you want to do the counting on.
Example:
F16=45+65+76 # Gives 186
F17=LEN(SUBSTITUTE(F16,"+","+")) # Gives 3

Excel conditional formatting for the entire row with more than one formula

After 3 hours of searching I still didn't find an answer, here is what I am trying to do:
I am trying to fill with green any row that has WBS in it and with Red any row that has ACT in it and Blue any row that has EPR in it. It works for the first formula then when I try to add the second one every thing get messed up.
what i have understood is that you need to search a keyword in a row and if its found in any cell of that row then color it.
May be we can do it with conditional formatting but i have another idea. We can create a simple search function in Excel VBA. Something like this:
=search_row(A1:F1,"EPR")
The function will return 1 if EPR is found in any cell of specified row. Now if you create two different columns at the end of data columns, name first with WPS and second with EPR and write this function in it. Like
G1 =search_row(A1:F1,"WPS")
H1 =search_row(A1:F1,"EPR")
Drag it to end. Now sort the columns. First for WPS from higher to lower. Then color all rows having 1 in a single select. Similarly do the same with EPR (H1) column.
To use this function you can download the macro file from the following URL:
http://asimishaq.com/myfiles/SearchHighlight.xlsm
Now to run it first of all enable macros, and then re-open your data file and then open this macro file. As long as this macro file is opened you can use this function. Following is the VBA code if you want to create the macro yourself:
Function search_row(sRow As Range, Keyword As String)
Dim i As Integer
Dim Found As Integer
For i = 1 To sRow.Columns.Count
If InStr(1, LCase(sRow.Cells(1, i)), LCase(Keyword)) > 0 Then
search_row = 1
End If
Next
End Function
I had a go at making a function similar to asim-ishaq to determine if your search term exists in the row for fun :) then tried to apply it to highlighting rows, turns out I dont know how to use conditional formatting very well! Figured it out in the end, hopefully I've explained it well enough.
With this you will have to have (one) extra column at the end of your data to contain the result.
It might be possible to not require the extra column by putting the function inside the conditional formatting, however I couldn't get it to work (didn't try very hard). This isn't a great loss as it's much simpler to edit the formula if its on the workbook, instead of having to go through each conditional rule to edit it, should you need to edit it in the future.
To get the formatting to work you will need to create a number of rules (one per keyword)
You want to create a rule of the type shown below, in the formula box you need something along the lines of: =INDIRECT("D" & ROW())=0 where D is the column containing the result of the function below and 0 is the index of the keyword you're highlighting.
In my example, the formula in the D Column is: =SearchRow(An:Cn,"ABS","KBS","JBS") (where n is the row the formula is on)
Set the formatting as desired then press OK, when you return to the rule manager you will need to update the Applies to value, which should be a range that covers all the data you want to highlight. In my example it was $A$1:$C$3
My function below takes 2+ Arguments, The first is the range to search. The second (and any subsequent ones) are search terms.
The function will return a number. -1 for no matches and 0+ for the found search term. The number depends on the position in the arguments.
A1 = "ABS"
B1 = "SBA"
A2 = "SBA"
B2 = "ABS"
A3 = ""
B3 = ""
C1 = "=SearchRow(A1:B1, "ABS", "SBA")"
C2 = "=SearchRow(A2:B2, "ABS", "SBA")"
C3 = "=SearchRow(A3:B3, "ABS", "SBA")"
C1 > 0
C2 > 1
C3 > -1
The function will always return the first result, searching left to right comparing each cell to the Keywords in order. Using my example, if a cell contained "SBA ABS" the result would be 0 (for ABS). I guess your cells will probably only contain one keyword though so this shouldn't be a problem?
Public Function SearchRow(ByVal Row As Range, ParamArray Keyword() As Variant) As Integer
Dim Column As Integer
Dim Value As String
Dim Index As Integer
Dim Result As Integer
For Column = 1 To Row.Columns.Count
Value = LCase(Row.Cells(1, Column))
Result = -1
For Index = LBound(Keyword) To UBound(Keyword)
If InStr(1, Value, LCase(Keyword(Index))) > 0 Then
Result = Index
Exit For
End If
Next Index
If Result > -1 Then
Exit For
End If
Next Column
SearchRow = Result
End Function

Resources