Repeat loop until empty cell - excel

I am looking for an Excel formula, that can add the results of rows untill it hits an empty cell.
The first row contains a the formula (including value X).
The following rows each contain 3 cells (A,B,C) with values in them.
It is simple True/False ("=IF(AND(X>=A2;X<=B2);C2;0)) that checks if 'X' > is higher than A, and lower than B, and if true, it returns the value of the C.
The formula then looks at the next row and repeats the action, and adds all the returned values to one sum.
ROW1 - A1(formula)
ROW2 - A2(5) - B2(10) - C2(100)
ROW3 - A3(10) - B3(20) - C3(200)
ROW4 - (empty)
ROW2 - If we set X=8, then 8 is > 5, and < 10, and the value 100 is returned.
ROW3 - The action is repeated - this time with the result 0.
ROW4 - The row/cell is empty and the loop stops.
ROW1 - Formula has stopped and shows result (100 + 0 =) 100.

This is exactly why they created the SUMIFS worksheet function.
For the whole column (if there's nothing after the first blank row):
=SUMIFS(C:C, A:A, "<" & X , B:B ,">" & X)
Note: X is the cell that X is located in.
The following will work if you do have stuff after the first blank row:
=SUMIFS(INDIRECT("C2:C" & MATCH(TRUE,INDEX(ISBLANK(A:A),0,0),0)-1),
INDIRECT("A2:A" & MATCH(TRUE,INDEX(ISBLANK(A:A),0,0),0)-1),"<" & M1,
INDIRECT("B2:B" & MATCH(TRUE,INDEX(ISBLANK(A:A),0,0),0)-1),">" & M1)

I'm not at a computer (with excel) right now.
I do know there is a select current region (I think it was mapped to Ctrl-/?) You should be able to use that to detect ends of contiguous regions. If you want only a column of that, use Range.Intersect to limit to a column (e.g. "$D:D" for column D).
This will leverage the power of Excel instead of coding your own.
Have a look at named ranges (Insert/Name, F3 to pick a named range in formula editor). IIRC you could have 'dynamic' ranges (like: automatically detect contiguous areas) under a name as well. I'd have to try how that worked to provide more details

if you're unsure of how many rows there are going to be then you may be better off writing vba to loop through the cells until it finds an empty one
myValue = 8
searchRow = 2
Total = 0
Do While Cells(searchRow, 1).Value <> ""
If (myValue > Cells(searchRow, 1).Value And myValue < Cells(searchRow, 2).Value) Then
Total = Total + Cells(searchRow, 3).Value
End If
searchRow = searchRow + 1
Loop
Cells(1, 1).Value = Total

Related

Counting rows based on individual columns, and the sum of multiple columns

In excel, I need to count the number of rows based on criteria from values in individual columns, and from the sum of a set of multiple columns.
For example, with the attached image of data, I want to be able to count the number of rows which are "valid" (= when Column A = 1), are over 17 years old (= Column B = >17), and when the sum of columns C to G are equal to zero.
I am currently using countifs function, with individual criteria for columns C to G being zero. However, with my actual data there are many more columns than C to G so the formula becomes too large, they are taking a long time to write and very vulnerable to mistakes.
I have tried using sumproduct but this has the same problem with a long formula.
Formulas I have tried;
=COUNTIFS($A:$A, 1, $B:$B, ">17", $C:$C, 0, $D:$D,0, $E:$E,0, $F:$F,0, $G:$G,0)
=SUMPRODUCT(($A:$A=1)*($B:$B>17)*($C:$C=0)*($D:$D=0)*($E:$E=0)*($F:$F=0)*($G:$G=0))
I know I could create a new column with an If function for the sum of columns C-G first (=IF(SUM(C2:G2)=0, 1, 0)), and then include this as a criteria (new column = 1) within a countifs, but I would like to avoid creating new columns with this data.
I would like to do =COUNTIFS($A:$A, 1, $B:$B, ">17", C:G, SUM(C:G)=0), but sum within countifs is not possible.
Any ideas would be very much appreciated?
Image of data:
Sample data:
| Valid | Age | a | b | c | d | e |
|-------|-----|---|---|---|---|---|
| 1 | 18 | 0 | 0 | 0 | 0 | 0 |
| 0 | 8 | 1 | 0 | 0 | 0 | 1 |
| 0 | 48 | 0 | 1 | 0 | 0 | 1 |
| 1 | 22 | 0 | 0 | 0 | 0 | 0 |
You said that you're unhappy with your COUNTIFS formula, but here's an efficient way to still use COUNTIFS (shorter/easier than VBA) and maintain accuracy.
The formula with your example would be:
=COUNTIFS(A:A,1,B:B,">=18",C:C,,D:D,,E:E,,F:F,,G:G,)
This is likely similar what you were using, but note:
If the criteria is "=0" then no criteria need to be specified between the commas.
If the function applies to the entire column, there is no need to specify row numbers.
In fact, even if there's other values
above/below your data (like headings), they'll be ignored
(unless they
meet all the criteria in the formula, which would be unlikely.)
Want to avoid errors? Make Excel write the formula for you!
If you're concerned about making errors when adjusting the formula to your actual data, let Excel do the work for you with three steps. (First save your workbook.)
Select each column (or a cell from each column) that you want to include in your formula. (To select multiple areas, click the first one and then hold Ctrl while you click the rest.)
Copy the line below by highlighting it and hitting Ctrl+C:
?"=COUNTIFS(A:A,1,B:B,"">=18""";:For Each c In Selection.Columns:?","&Columns(c.Column).Address&",";:Next c:?")"
Hit Alt+F11 then Ctrl+GV then Enter (This immediately runs the VBA code from Step 2.)
The formula will appear on the next line. Just copy/paste into to where you want it!
Example output:
=COUNTIFS(A:A,1,B:B,">=18",$C:$C,,$F:$F,,$G:$G,,$P:$P,,$T:$T,,$AJ:$AJ,,$AC:$AC,)
Edit:
I'm not sure why you want/need to use INDIRECT but it's fine as long as you maintain the same syntax. A simplified example:
I have values in Columns A and B. I want to count rows where A=1 and B=1. My COUNTIFS formula (in D2) would be: =COUNTIFS(A:A,1,B:B,1)
The reason to use INDIRECT could be if, for example, I don't always want this to use Column B -- I want to be able to specify which column to use, in a different cell, maybe D5.
In D5 I enter the text: B:B. Then I change the formula is D2, replacing B:B with INDIRECT(D5).
Since E2 contains the text B:B, the formula is still indirectly referring to B:B, and the result of the formula does not change. If I specify a different column range in E2, the formula will look at the new range.
Important Note:
With SUMIFS, COUNTIFS and AVERAGEIFS, all ranges specified must have the same number of rows and columns as the criteria_range1 argument.
In this formula, that means they must be entire columns.
If you're just trying to refer to a column on a different worksheet, that's fine as long as you use correct syntax, such as: =COUNTIFS(A:A,1,Sheet1!D:D,1) to refer to column D on Sheet1.
If the name of the sheet is contained as text in a cell (D5), but you want the column (D:D) still hard-coded, then your INDIRECT section of the formula would be: INDIRECT(D5&"!D:D")
If the specified worksheet name has a SPACE in it, then you need to surround the name with ' apostophes, in the correct spot:
=COUNTIFS(A:A,1,INDIRECT("'" & D5&"'!D:D"),1)
...which is one of the many reasons spaces and other non-alphanumeric characters should be avoided when naming "anything".
This can easily be done with a custom function using VBA. A custom function is a self programmed function that can be used as a normal function in an Excel cell. the function is called countCorrectEntries and takes a Range as Parameter. So if your table(including header) is located in A1:G5 the function in your target cell would be =countCorrectEntries(A1:G5) What it essentialy does is, it initiallizes a variable countCorrect=0 which is going to count all rows that fullfill all conditions.
The loop:
For i = 2 To UBound(table, 1)
...
Next i
Goes through the rows and checks the conditions that look as follows:
If table(i, columnNumber) <> condition Then
conditionsMet = False
End If
It checks in row i (the iterator) if in a column e.g 1 for first does not meet a certain condition. E.g value is not equal to 1. If that is so, a boolean conditionsMet is set to False and it is not counted as correct.
For your sum problem, I propose the following condition:
'Check sum
tempSum = 0
For j = startColumn To EndColumn
tempSum = tempSum + table(i, j)
Next j
If tempSum > 0 Then
conditionsMet = False
End If
The loop within the loop goes over the columns between startColumn and endColumn e.g 3 and 7 for column C to G and sums the values in tempSum. Then it is checked if the sum is greater 0 and again the conditionsMet is set to False if so.
If after all the conditions are checked the conditionsMet is still True then CountCorrect is incremented by 1.
The following code should work for your problem. Just change the inner loop values for the correct columns and you are all set.
Cheers!
Function countCorrectEntries(Rng As Range)
Dim table As Variant
table = Rng
countCorrect = 0
For i = 2 To UBound(table, 1)
conditionsMet = True
If table(i, 1) <> 1 Then
conditionsMet = False
End If
If table(i, 2) < 18 Then
conditionsMet = False
End If
'Check sum
tempSum = 0
For j = 3 To 7
tempSum = tempSum + table(i, j)
Next j
If tempSum > 0 Then
conditionsMet = False
End If
If conditionsMet = True Then
countCorrect = countCorrect + 1
End If
Next i
countCorrectEntries = countCorrect
End Function

How do I input values in the Excel model without doing it manually?

I have the feeling that my previous question is being misunderstood. Therefore, I will do this as follows. What I would like is the following:
In this picture you see that the input is 4 in cell B1. This B1 cell is used as an input for a complicated model in a different sheet. The output of that model is linked to B2. I do not want to modify the complicated model on the other sheet (this part is required).
Now I would like to create a table like this:
So when the input is 4, the output from the model in the other sheet is 1. Now I can do this manually for 1, 2 and 3, by simply replacing the number written at B2 (which is 4) to 1 and check its output. Let’s observe what our complicated model gives us:
Apparently it gives us a 9, so I fill 9 in the F2. Now this is currently not a problem, since my table goes from 1 to 4. But how do I automate this? For example if I go from 1 to 100.
The problem is, you cannot touch or simplify the model on the other sheet.
How would one do this in VBA or do this in Excel itself?
I think you might have an issue in your explanation. Your example is A1 = 1, B1 = A1+5, despite saying it's A1 & A2. You are saying "Columns C & D" when I believe you mean "Rows 3 & 4).
If I understand your plight correctly, you are looking to drag down (auto-fill) the formula to the subsequent rows. You can do this with VBA, but you can do it without.
Provided you have data in the first column (A) as far as you would like the formula to travel, you can double-click from the lower right hand corner of the formula-reference cell and it will fill down. You could also drag the formula down by clicking and holding the lower right hand corner of the formula-reference cell.
You will know if you have the lower right hand corner if your cursor changes from from a "+" that is relatively large (with interior color) to a "+" that is relatively small (with no interior color... all black).
You will need to ensure that you have relative references when doing this, or ensure that your non-relative references are what you want. This is more than your question asked, but is important when doing this type of work.
See this:
Range("B1").Formula = A1 + 5
In this formula, A1 is relative to B1 by an off-set of -1 column. Every cell that the formula is pasted or dragged into will perform the formula with the cell that is -1 column relative.
You can add specifics to columns, rows, or cells, by use of "$". Say you have your example, and want to show the formula in Column C. There're 3 scenarios by using "$" which have different outcomes:
Fully relative, the dragged-formula will automatically designate the adjacent column.
Range("B1").Formula = A1 + 5
Range("C1").Formula = B1 + 5
Range("B2").Formula = A2 + 5
Range("C2").Formula = B2 + 5
If the "$" is in front of the column in the formula, the-dragged will "lock" the column, so when the formula is dragged, the column stays the same, but the row number will change.
Range("B1").Formula = $A1 + 5
Range("C1").Formula = $A1 + 5
Range("B2").Formula = $A2 + 5
Range("C2").Formula = $A2 + 5
If the "$" is in front of the row in the formula, the-dragged will "lock" the row, so when the formula is dragged, the row stays the same, but the column will change.
Range("B1").Formula = A$1 + 5
Range("C1").Formula = B$1 + 5
Range("B2").Formula = A$1 + 5
Range("C2").Formula = B$1 + 5
If the "$" is in front of the each the column and row in the formula, the-dragged will "lock" both. When the formula is dragged, the referenced-cell stays the same.
Range("B1").Formula = $A$1 + 5
Range("C1").Formula = $A$1 + 5
Range("B2").Formula = $A$1 + 5
Range("C2").Formula = $A$1 + 5
Hopefully that helps and is what you're looking for!
So you want to automate putting values through your model and recording the outputs.
A very simple approach begins with putting your list of inputs in column E as in your example picture. Note down the start and end row numbers - 2 and 5 in your example (as your input values are in the range E2:E5).
In the VBA editor created a new sub in a new module to hold your code (you can Google how to do this). The code is fairly simple.
Sub TurnInputsIntoOutputs()
' First we create some useful and important variables
Dim inputStartRow As Long ' The row your input values start on
Dim inputEndRow As Long ' The row your input values end on
Dim currentRow As Long ' A placeholder for when we cycle through the rows
Dim processingWorksheet As Worksheet ' How we're going to reference your worksheet
' Then we put values into those variables
inputStartRow = 2
inputEndRow = 5
Set processingWorksheet = ThisWorkbook.Sheets("*the name of your worksheet here*")
' Now we cycle through the input rows
For currentRow = inputStartRow to inputEndRow
' Put input value from the current row of column E into cell B1
processingWorksheet.Range("B1").Value = processingWorksheet.Range("E" & currentRow).Value
' Put the resulting output value into the current row of column F
processingWorksheet.Range("F" & currentRow).Value = processingWorksheet.Range("B2").Value
Next currentRow
End Sub
And then just run your macro (you can link it to a Form button or just run it from the VBE - easy enough for your to Google how to do).

Returning a date cell if a sum a range of a row is equal to something

I have a table that could go infinitely long, that has three categories per date. I need to figure out a way to have a cell return its date the earliest it reaches a numerical threshold (six in this case, or anything greater than five).
So with my specific workbook I’m working on (here), column AM through CH could go on forever. I would like to figure out a function to have 6th IV (Column AL) be the date (Row 1) the cumulative 6th ‘IV’ took place. So for the 6th row AL6 would be 3/17/14, because the sum of each ‘IV’ (or column with a 3 in Row 3) hits 6 on cell BD6. One reason this is throwing me off is that I can’t just use countifs, so for example, Row 8, there are 2 ‘IV's on 2/3/14 (cell AO8), so the 6th ‘IV’ is on 3/3/14 (the 6th one being in cell BA8).
Does this make sense to anyone? Any ideas, thoughts, comments, concerns?
EDIT: Solution
Public Function fn_FindDate(ByVal SearchRange As Range)
Dim Total As Integer
Total = 0
For Each Cell In SearchRange
If Not IsNull(Cell.Value) Then
Total = Total + Cell.Value
End If
If Total >= 6 Then
Cell.Font.ColorIndex = 3
fn_FindDate = Range(Chr(64 + Cell.Column) & 1).Value
Exit Function
End If
Next Cell
fn_FindDate = ""
End Function
In Column A, you put the formula =fn_FindDate(AM2:CH2) and you should be good! Feel free to remove the Cell.Font.ColorIndex = 3 if you don't want the 6th IV to turn red.
It's also set up to recognise when it PASSES 6. If you have a 1, a 1 and a 5 in a row, the 5 will trigger, even though it technically skips from 2 to 7. Also, it will return a blank value if the '6' threshold hasn't been met in a row, so you can just copy-down and it won't look sloppy.
Hope this helps!

How to include another if function within a combined if, vlookup and index function?

My data looks like:
Colums C,D,E,F
Row 1 4,10,40,F
Row 2 4,12,48,F
Row 3 4,14,56,F
Row 4 3,16,48,F
Row 5 1,18,18,F
Row 6 1,20,10,F
Row 7 0,22,0,0
My foucs is on column F. My current array formula in column F is:
=IF(C31=0,VLOOKUP(INDEX($C$30:$C$38,MIN(($C$30:C30*D31)-$E$30:E30)),$C$30:$E$38,1),"F")
In case column C is is equal to 0, I want the formula of column F to return the value of column C,which has the respective minimum difference between column C (allrowsbefore) * column D (currentrow) and E(respective, all rows before). For instance, in row 2 that minimization problem would equal:C1*D2-E1, if C2 would be equal to 0.
However, now i want column F formular to only choose a value of C which is unequal to 0.
Hence, in column F7 i want column F to give me the value 1, as this is a C value which is bigger as 0 and has the respective min difference between (C6*D7)-E6.
(For value 4 of C it would be 32 and for value 3 of C it would be 18).
How and where do i include the if condition, that the value of C chosen has to be bigger as 1, into my existing formula ?
Thanks a lot for helping out, its much appreciated!!! In case i need to split up my existing formula that would be alright, too.
I reread your question more carefully after making my earlier comment, and I think I understand better now. To make sure you get the smallest value greater than 0, you use the following according to this KB article:
=MIN(IF(A1:A10>0,A1:A10))
So your formula in F7 should look like this for the ranges you described:
=IF(C7=0,VLOOKUP(INDEX($C$1:$C$7,MIN((IF($C$1:$C$7>0,$C$1:$C$7)*D6)-$E$1:$E$7)),$C$1:$E$7,1),"F")
EDIT
Okay. I think I finally understand the question. :)
If Cx <> 0, Fx = Cx
If Cx = 0, Fx = the address of the cell in Column C that produces minimum of (C1 * D7 - E1, C2 * D7 - E2, ..., CN * D7 - EN)
From what I know of Excel, I don't think you can do this with a single formula. INDEX and VLOOKUP both look for values in the worksheet, but you're trying to work from the value of a calculation (MIN). Using the array formula to find that value is really smart. But once we get the value (4 in this case), we can't use that to look up the address of the cell in column C that produced that result. I really can't think of any way to do this without a macro. Are you able to do that?
EDIT: Macro Solution
This is a little quick and dirty, but it passed the test. Make sure to save your workbook as a Macro-Enabled workbook. Hit Alt+F11 to open the macro editor. You might need to insert a new module. You can do this by right-clicking the workbook project in the project explorer on the left.
Once you've done this, double-click on the module. You should get a blank white text editor on the right. Paste the following code into that window:
Public Function MinimumC()
Dim rngCurrent As Range
Set rngCurrent = Application.ThisCell
Dim rngMin As Range
Dim minimum As Long
minimum = 100000000
Dim tmp As Long
Dim rngC As Range
Set rngC = ActiveSheet.Range("C1:C" & rngCurrent.Row - 1)
For Each c In rngC.Cells
If c.Value2 <> 0 Then
tmp = c.Value2 * rngCurrent.Offset(0, -2).Value2 - c.Offset(0, 2)
If tmp < minimum Then
minimum = tmp
Set rngMin = c
End If
End If
Next c
MinimumC = rngMin.Value2
End Function
Save the workbook. You can now close the code editor.
Now you can use the following formula in F1 and copy down column F: =IF(C1<>0,C1,MinimumC()).
This will work as long as the value of C1 * Dy - E1 is never greater than 100000000. :)

Dynamic insert function

It is a requirement that I use Excel to solve this issue.
In col A I have 0s and 1s with various quantities of 0s between the 1s. Every time a 1 appears I want the difference between two numbers given in two columns next to my binary column. However I wish to get the results from this calculation stated next to the previous 1.
I'd cope with different software, but how do I achieve this with Excel?
=IF(A4=1,OFFSET(B4,MATCH(1,A5:A$1000,0),0)-OFFSET(C4,MATCH(1,A5:A$1000,0),),"")
in D4 and copied down to suit seems to work.
Edit:
=(IF(A$=1, ,"") is as in: IF(logical_test,value_if_true,value_if_false) where value if false is (blank), expressed as "".
The value_if_true is the difference between ColumnB and ColumnC values, each ‘located’ from an OFFSET function as in =OFFSET(reference,rows,cols,height,width).
references are to the appropriate column for the row into which the formula is inserted (ie B4 and C4) from which the values required are ‘south’ by a variable amount.
MATCH, as in =MATCH(lookup_value, lookup_array, [match_type]) is to determine the extent of the offset on a case-by-case basis. In reverse order, the parameters here are match_type = 0 (to require an exact match) and lookup_array is as much of ColumnA as required. Initially chosen as up to Row1000 (by A$1000) but can be extended as far as necessary, subject to row limit for the relevant Excel version.
The first parameter lookup_value) is of course 1 since that is the flag for the rows that contain the values to be subtracted.
Without a $ between A and 5 in the MATCH functions the size of the array automatically decreases (top cell row reference increases) as the formula is copied down, hence finds the next instance (rather than the same one over and over again).
With VBA, I'd first set the formulas to show results in same line as the "ones". (Suppose I used the D column for that.)
= if(A1 = 1; B1 - C1; "")
Then, in VBA window, do the following:
Dim i as integer
Dim Filled as Collection
Set Filled = new Colleciton 'this collection will stored filled lines
'store filled lines
for i = 2 to 1000 'use your table limit
if Sheet1.Cells(i, 4).Value <> "" then 'the 4 is for D column, i for the line
Filled.Add i
end if
next
'now fill the E column with displaced values
for i = 1 to Filled.Count - 1
Sheet1.Cells(Filled(i), 5).Value = Sheet1.Cells(Filled(i+1), 5).Value
next
'please note there's a missing line (the last), it's up to you to decide how to fill it
'sorry I cannot debug this code
I'd associate that to some sheet event or to a button.

Resources