Break from for loop back into if statement vba - vba

I am trying to perform an action that will see if the date in range (dateRng) is less than today's date, and then, if it is, perform the for loop to hide the rows w.here a value in the column is zero. (I am paying off loans, and every month I want it to hide any loans that have been paid off.) Months are across columns, loans are in rows. Loan balance is (i, j).
The problem is that it never exits the for loop to go back and check the date after every new 'j' (column). It just stays in the for loop. I have tried break, exit, continue, etc. None seem to work, at least where I place them. How do I get it to check for the date, compare to 'today', THEN run the for loop to check each cell in the column, before moving on to column 2, checking the date and performing the same for loop.
It would be good to have it be dynamic, but that is not necessary, as every month I could just change the ranges in the code. This is strictly for my personal use. Any help is appreciated. thank you.
Sub hidePaid()
Dim day As Range, loanRng As Range, loanSum As Worksheet, dateRng As Range, cel2 As Range, i As Long, j As Long, col As Range
Set loanSum = ThisWorkbook.Worksheets("Loan Sum")
loanSum.Activate
Set dateRng = ActiveSheet.Range("D2:R2")
Set loanRng = ActiveSheet.Range("D4:R16")
For Each day In dateRng
If day.Value < Date Then
For j = 1 To loanRng.Columns.Count
For i = 1 To loanRng.Rows.Count
If loanRng.Cells(i, j).Value < 1 Then
loanRng.Cells(i, j).EntireRow.Hidden = True
End If
Next i
Next j
End If
Next
End sub

I added comments in the code to show my changes.
You were close, but had one to many loops and like you say, needed to find the right place for the exit.
Sub hidePaid()
Dim day As Range
Dim loanRng As Range
Dim loanSum As Worksheet
Dim dateRng As Range
Dim i As Long
Set loanSum = ThisWorkbook.Worksheets("Loan Sum")
loanSum.Activate
Set dateRng = ActiveSheet.Range("D2:R2")
Set loanRng = ActiveSheet.Range("D4:R16")
'This loop processes by column
For Each day In dateRng
'Once the date in the column is greater than today, it will stop processing
'It assumes the values in dateRng are valid dates
'(I.e. '01/01/2016' not just 'Jan', you can use number format in Excel to
'get a date to show as 'Jan' if that is better for you)
If DateDiff("d", Now(), day.Value) > 0 Then Exit For
'The line of code you had should have worked in sense,
'it would have touched every column but only procesed those before today
'It also assumes that value in the cell to be an actual date
'If day.Value < Date Then
'You do not need a column loop here as you are already in one in the
'previous loop
'For j = 1 To loanRng.Columns.Count
'This loop processes all the rows that are not already hidden and if
'the value is equal to 0 then it hides the row
'Note: you had the check to be less than 1, .50 is less than 1 and you don't
'want to get caught out on a loan!
For i = 1 To loanRng.Rows.Count
If (loanRng.Cells(i, day.Column - 3).Value = 0) And (loanRng.Cells(i, day.Column - 3).EntireRow.Hidden = False) Then
loanRng.Cells(i, day.Column - 3).EntireRow.Hidden = True
End If
Next i
Next
'Its good practice to clear out resources when finishing
Set dateRng = Nothing
Set loanRng = Nothing
Set loanSum = Nothing
End Sub

Related

Using VBA DateDiff to compare multiple Dates, output the difference and then compare output against another cell

I've got a spreadsheet which is used to record multiple times/dates where services were rendered.
In the spreadsheet the columns I'm interested in comparing start at row 9, column BA-BB, BC-BD, BE-BF, BG-BH, BI-BJ, BK-BL, BM-BN, BO-BP, BQ-BR for each of the rows in minutes. I then want to add all the total differences between the dates and finally compare that total with with AF9 if populated or if that cell is blank AG9.
I want the Macro to loop through all the rows producing a total units for each row at the end of the sheet (Column BU)
The purpose of the spreadsheet is to check that the value populated in either AF or AG is in fact correct if we were to work out the difference in times and convert to units anyway.
What I've been working on so far is:
Sub CalculateDate()
Dim Result, RowNo As Long
Dim FirstDate, SecondDate As Date
Dim ws As Worksheet
Set DateCompare = ActiveWorkbook.Sheets("Master")
Set DateCompareRng = Support.Range("BA2", Support.Cells(Rows.Count, "BA").End(xlUp).Offset(0, 18))
Set DateCompareArr = DateCompareRng.Value2
RowNo = 1
Do Until DateCompare.Cells(RowNo, 1) = ""
FirstDate = DateCompare.Cells(RowNo, 1)
SecondDate = DateCompare.Cells(RowNo, 2)
DateCompareArr(FirstDate, 3) = DateDiff("m", FirstDate, SecondDate)
RowNo = RowNo + 1
Loop
End Sub
The above is my shoddy attempt at amending some logic someone else provided on the forums to a similar question. I don't want to compare specific dates I enter though as the dates will all be different throughout the cells.
I've never used this type of function before in VBA so not really sure on how to go about changing it to suit my needs. If I can manage to loop through of of the start/end times I can probably work out how to loop through additional columns and compare against another 2 columns after that.
Some sample date is:
Start 1 | Start 2
23/03/2018 12:00 | 2018-03-23 16:00 GMT
Difference = (In minutes)
Compare Difference to:
Total Units(Column AF) = 600(this is 600 minutes)
Sorry that this is such a long question. I'm just really stuck with getting started on this problem
I like your attempt, you are on the right track. Below is tested sample code, which I think will provide you with the answer you're seeking. Good luck and happy coding
Public Sub CalculateDate()
'While I don't recommend hard coding the start and end of your range
'for this example, I thought it would simplify things.
'Start of the range is the first cell, which in your example seems
'like BA9
Const RANGE_START As String = "BA9"
'End of the range is the last cell in right most column, which
'in your example was BR. I chose the 18th row, but you could
'make it whatever you need
Const RANGE_END As String = "BR18"
'Declare a worksheet variable as you've done
'And set it to the worksheet in the ActiveWorkbook as you've done
Dim ws As Worksheet
Set ws = ActiveWorkbook.Sheets("Master")
'Declare the range that contains the values you need to sum
Dim rngToSum As Range
'And set it to the range in the WorkSheet
'In this case the range will be
'ws.Range("BA9:BR18")
Set rngToSum = ws.Range(RANGE_START & ":" & RANGE_END)
'Declare an index to be used in the for loop below
'as we loop through each column in the row the
'code is summing
Dim nDx As Integer
'Declare a range for the row to be worked on
Dim rngRow As Range
'Declare a string value that will hold the
'output range(row, cell)
Dim outStr As String
'Declare an output range variable
Dim outRng As Range
'Declare a variable to hold the summation of the
'row values you want to add together
Dim rowSum As Long
'Outter loop to loop through each of the rows in the
'defined range
For Each rngRow In rngToSum.Rows
'Initialize/Reinitialize the rowSum to 0 for each row
rowSum = 0
'Inner loop to loop throug all the columns in the range
'you want to add together
For nDx = 1 To rngToSum.Columns.Count Step 2
'NOTE--> DateDiff uses lower case N for minutes, not lower case M
'I noticed that in your sample code
rowSum = rowSum + DateDiff("n", rngRow.Value2(1, nDx), rngRow.Value2(1, nDx + 1))
Next
'Completed adding all the columns together
'Assign the outPut row, cell for the output Range
'The formula below will concatenate the
'letter A with the current row number
'For example if the current row number is 9
'outStr will equal A9
outStr = "A" & rngRow.Row
'I always use Value2 since it is faster than the
'Text or Value properties of the range object
ws.Range(outStr).Value2 = rowSum
Next
End Sub

How can I UPDATE a date based on a cell change in Excel?

This is a little background. In Army Aviation we have to conduct a Night Goggle (NG) flight at least once every 60 days. This date is gathered from the last time an NG flight is conducted in order to display and track currency with the mode.
With the picture linked above as reference (screenshot of actual book), I'm trying to UPDATE the date on another tab (Display Panel) with the DATE (B COLUMN) (B28) based on the INPUT (> or = 1.0) placed in the CELL (P COLUMN) (P28).
And with that, any time another NG hour entry is made below in the same column, it'll perform the script again, provided the aforementioned applies.
I've tried banging out a few VBA lines, but I'm not having any luck.
This is the shell of what I think you need which goes in the code pane associated with the sheet which you are monitoring column P of...
It fires if there is a change in column P and if the value is >=1
There is 1 further bit of logic. I am only updating C32 if the new value is greater than the existing.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim updateCell As Range
Dim lastDateCell As Range
Set updateCell = ThisWorkbook.Worksheets("Display Panel").Range("C32")
Set lastDateCell = ThisWorkbook.Worksheets("Display Panel").Range("AA32")
If Target.Column = 16 And Target.Value >= 1 Then
If Target.Offset(, -14) > updateCell Then
lastDateCell = updateCell
updateCell = Target.Offset(, -14) 'only update if greater than existing
End If
End Sub
Code in code pane of Flight Log sheet:
(Posted the solution on behalf of the question author).
Below is what I added.
Private Sub Worksheet_Change(ByVal Target As Range)
Sheets("Flight Log").Unprotect "Password Here" 'This unprotects the sheet to allow changes
Dim updateCell As Range
Dim lastDateCell As Range
Set updateCell = ThisWorkbook.Worksheets("Computations").Range("A36") 'This is the primary date location
Set lastDateCell = ThisWorkbook.Worksheets("Computations").Range("A40") 'This is the backup date location
If Target.Column = 16 And Target.Value >= 1 Then 'This check P column for the value
If Target.Offset(, -14) > updateCell Then 'If "P" returns the value, date in "B" is grabbed
lastDateCell = updateCell 'This updates the primary date cell
updateCell = Target.Offset(, -14) 'This ensures date is not overridden by lesser value
End If
End If
Sheets("Flight Log").Protect "Password Here" 'This line reprotects the sheet
End Sub
Additionally, I use two LOOKUP functions to evaluate the HOUR(s) in reference to the dates grabbed by the VBS:
=IFERROR(LOOKUP(A36,'Flight Log'!B:B,'Flight Log'!P:P),"/// NG DATE ERROR ///")
and,
=IFERROR(LOOKUP(A40,'Flight Log'!B:B,'Flight Log'!P:P),"/// NO B/U DATE ///")
after this, I use another IF function to verify the HOUR wasn't miss-entered
=IF(A37>=1,A36,IF(A41>=1,A40,"NG HOUR ERROR"))
The simple IF functions check and verify the HOUR wasn't entered incorrectly by the Pilot--which would prompt the VBS script to grab that DATE--and then re-entered to it's intended "correct" value. For instance, "Pilot enters time into block, pilots realizes time he entered was wrong, and corrects it." This will keep his error, from being an overall error.

Using VBA to find start value, count rows till that value becomes 0 and record result. Repeat for same column until the end of the data reached

I'm a newbie to VBA/coding in general and my usual tactic of sticking bits of pre-written code isn't working for my problem.
I'm looking to create a macro that will do 3 things:
Allow me to find a starting point for the data in a column.
Start counting the number of rows once the cell value has
changed to a constant.
Once the value moves back to the starting point for the count to stop and record the number of cells counted in separate column with positioning of the count in that column at the start point of the count.
Repeat until the end of the data.
For this case the start point will be when the cell has a value of >0.
It will increase to a constant number (300).
Once at 300 the macro will have to count the number of rows that contain the numerical value 300 until the value goes back to 0.
Report count in a separate table on the worksheet with the entry being input at the same relative position in the new table as when the count started from the data.
And finally the loop.
I need to also do a similar count but in the horizontal direction (i.e. counting columns on a row). If anyone can create a code for the vertical/row count problem above I'd really appreciate it if you could annotate it so I can attempt to understand/learn which bits of code carry out each action and thus change it up for horizontal/column count.
I've attached a screenshot of the spreadsheet however as a new user it must be as a link. The blue highlighted table is the data used for the vertical /row count problem I am talking about. The blank table underneath the highlighted table has manually inputted correct answers for the first column of data for what I would like the macro to do in case I haven't accurately described my request.
I have also attached the horizontal table with correct manually inputted answers for row 1 in the separate table for the column count along the row.
Lastly, here is the code that I have written to tackle the problem, however it is very basic and won't run.
Sub Count0()
For Each c In Worksheets("Sheet1").Range("D30:D39")
If c.Value = 0 Then
End If
If c.Value > 0 Then
v = Range(c.Value)
For i = 3 To Rows.Count
If Cells(i, 1).Value <> v Then
MsgBox CStr(i - 2)
End If
Next i
Next c
End Sub
This worked in the limited case I tested (two columns and several rows in different patterns. It's pretty basic--there are more elegant ways to do it.
Sub Count0()
'To hold the current cell
Dim current As Range
'To hold the total number of rows and columns having data
Dim rows As Long
Dim cols As Long
'To iterate across rows and columns
Dim r As Long
Dim c As Long
'Flag/counter variables
Dim found As Long 'Saves row on which first "constant" was found
Dim count As Long 'Saves count of "contants"
'Use SpecialCells method to obtain the maximum number of rows and columns
' that have data.
cols = Worksheets("Sheet1").Cells.SpecialCells(xlCellTypeLastCell).Column
rows = Worksheets("Sheet1").Cells.SpecialCells(xlCellTypeLastCell).Row
'Increment through all columns that have data. This is a bit inefficient
' because it really isn't necessary to go through all the empty cells,
' but it works.
For c = 1 To cols
'Initialize flag/counter
found = 0
count = 0
'Increment through all rows for the current column.
For r = 1 To rows
'Examine the current cell
Set current = Worksheets("Sheet1").Cells(r, c)
'For positive values, save the first row that has the value
' and count the number of values.
If current.Value > 0 Then
If found = 0 Then found = r
count = count + 1
End If
'When the next non-positive value is reached--OR the end of the
' row is reached--and there was a constant found, write the count
' to the next worksheet in the cell corresponding to the row and
' column having the first instance of the constant.
If (current.Value <= 0 Or r = rows) And found > 0 Then
Worksheets("Sheet2").Cells(found, c).Value = count
'Reset the flag/counter
found = 0
count = 0
End If
Next r
Next c
End Sub
I was struggling with what you had written, and ended up doing this in the end. I left you variables for changing the sheets to read from and print to (assuming you can print the results to another sheet- if not it should be easy enough to change).
This should also work for all cells in your range, assuming that there are values in all boxes.
Problems I noted with your original code were:
The first if did nothing
I'm pretty sure you shouldn't use numbers in sub/function names
Dimensioning no variables is a bad idea
Anyway, give me a comment if you need any help (and well done for writing a good first question).
Sub CountZero()
Dim SourceSheet As Worksheet, SummarySheet As Worksheet
Dim CurrentCell As Range
Dim FirstRow As Long, LastRow As Long
Dim FirstColumn As Long, LastColumn As Long
Dim TotalValues As Long
Set SourceSheet = Worksheets("Sheet1")
Set SummarySheet = Worksheets("Sheet2")
FirstRow = 1
LastRow = SourceSheet.Range("A" & rows.count).End(xlUp).row
FirstColumn = 1
LastColumn = SourceSheet.Cells(1, Columns.count).End(xlToLeft).column
For col = FirstColumn To LastColumn
For Rw = FirstRow To LastRow
Set CurrentCell = SourceSheet.Cells(Rw, col)
If CurrentCell <> 0 Then
TotalValues = ProcessSection(CurrentCell)
SummarySheet.Cells(Rw, col).value = TotalValues
Rw = Rw + TotalValues
End If
Next Rw
Next col
End Sub
Function ProcessSection(FirstCellWithValue As Range) As Long
Dim Counter As Long: Counter = 0
Do Until FirstCellWithValue.Offset(Counter, 0).value <> FirstCellWithValue.value
Counter = Counter + 1
Loop
ProcessSection = Counter
End Function
As a small disclaimer, I haven't tested this, let me know if there are problems.

Why does my VBA code keep hanging?

I attached two links to a flowchart of how the VBA code should go about, and a screenshot of my two sheets.
Basically, I have two sheets - "Disbursements" & "Cheque Info". On the Disbursements' sheet, I need to consider only the rows with count > 1 (column I). For example, I won't consider row 8 of column I, but will consider row 12. Every row that has a count of > 1 should have a value on row H by the end of the run.
After considering which row has a count >1, we check if the corresponding amount (column F) is equal to Cheque Info's column E. Then if for example, for row 12 of disbursements, 1,384.35 is equal to row 9 of Cheque Info. We must get the difference of these dates then store it to a variable "Current". But there are many "1,384.35" that we must get the minimum difference for the dates, thus a need for a loop.
Again, I need to do loops for each row that has a count of >1 on disbursements' column I, so that I will get the date on Cheque Info (with the same amount) that has a minimum gap from the date on Disbursements sheet. For example, the date that has the least gap for 1/18/2016 (for the amount 1,384.35) is 1/4/2016.
Here is my current code:
Sub F110Loop()
Dim x As Integer 'current amount
Dim y As Integer
Dim d As Double 'delta between Disbursement date and Cheque Release date
Dim Current As Integer
Dim Least As Integer
Dim Dis As Worksheet
Dim Cheque As Worksheet
Dim wb As Workbook
Set wb = ThisWorkbook
Set Dis = wb.Sheets("Disbursements")
Set Cheque = wb.Sheets("Cheque Info")
wb.Activate
For x = 4 To 600
Do While Dis.Cells(x, 9).Value > 1
'IF same amount, get row number to get corresponding date, reference that date
For y = 3 To 600
If Dis.Cells(x, 6).Value = Cheque.Cells(y, 5).Value Then
'THEN get delta
Current = Dis.Cells(x, 4).Value -Cheque.Cells(y, 2)
'IF current is less than the least delta
ElseIf Current < Least Then
'THEN update new value of delta
Current = Least
Else
'copy paste the date (from the least delta row)
Cheque.Cells(y, 2).Copy Destination:=Dis.Cells(x, 8)
End If
Next y
Loop
Next x
End Sub
Your code hangs because you dont have any check s for null/vbnullstring or 0 values. i.e.:
IF Dis.Cells(x,6).Value <> vbNullString OR Dis.Cells(x,6).Value <> 0 Then....
reason this is needed is to keep excle from looping through each cell till out of memory...
Your Do Whileloop is an infinite loop. Once it catches a cell such that Dis.Cells(x, 9).Value > 1 it will loop forever, because inside the loop, nothing will change, neither x nor Dis.Cells(x, 9).Value.
I think you have to think again the logic of the subroutine. Maybe replacing that loop with a simple IF test would do.
The Do Loop will not exit until Dis.Cells(x, 9).Value > 1. Inside the Do Loop you change values in Dis.Cells(x, 8). If Dis.Range("I3:I600") does not have formulas in it or if anyone of the cells in Dis.Cells(x, 9).Value never exceed1 then theDo Loop` will never exit.
Do While Dis.Cells(x, 9).Value > 1
'IF same amount, get row number to get corresponding date, reference that date
For y = 3 To 600
Next y
Loop
You should also turn off ScreenUpdating while the code is running. If you don't need any formulas to recalculate then set Calculation to xlCalculationManual.
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Why are you using Range.Copy?
Cheque.Cells(y, 2).Copy Destination:=Dis.Cells(x, 8)
A direct assignment is much more effecient
Dis.Cells(x, 8) = Cheque.Cells(y, 2)
If there are no formula that you need to recalculate then using an array instead should cut you execution time to under 1 second.

Excel Macro: Selecting a specific Row based on column date

I am writing my first macro and have a question on how I can select a specific Row based on a value in a specific column. here is my code so far:
Sub Pipeline()
'Module 3
'Iterating through the Funding Date Column and looking for clients going live within 30 days
'Selecting the rows for each client in that target range
'TODO: Export information into an email template in Outlook
'TODO: Send email to distribution list
Dim fundingDate As range
Set fundingDate = range("M4:M500")
Dim todaysDate As Date
todaysDate = Date
For Each cell In fundingDate
If cell < todaysDate + 30 Then
'Need to select the entire row
Else
cell.Font.ColorIndex = 3
End If
Next
End Sub
replace 'Need to select the entire row with
cell.entirerow.select
UPDATE
Here is a much more efficient way to get what you need without all the looping.
In your code Replace from For Each cell ... to Next with this:
With fundingDate
.AutoFilter 1, "<" & todaysDate + 30
.SpecialCells(xlCellTypeVisible).Select
'here are your clients going live in next 30 days
.AutoFilterMode = False
End With
You may need to provide some error checking in case you don't have clients going live within 30 days (SpecialCells method will fail on this) and also, if M4 is not your column header, you may want to adjust how the range picks up the visible cells.