Macro - delete rows based on date - vba

I am very new to VBA and macros in Excel. I have a very large excel spreadsheet in which column A holds dates. I am trying to delete the rows which have a value smaller than a certain date and this is what I have come up with till now..
Sub DELETEDATE()
Dim x As Long
For x = 1 To Cells.SpecialCells(xlCellTypeLastCell).Row
Debug.Print Cells(x, "A").Value
If CDate(Cells(x, "A")) < CDate("01/01/2013") Then
Cells(i, "A").EntireRow.Delete
End If
Next x
Next i
End Sub
I am receiving a Next without For error... can somebody help please?

This lends itself well to using the .AutoFilter property of a Range. The script below contains a comment for each step taken:
Option Explicit
Sub DeleteDateWithAutoFilter()
Dim MySheet As Worksheet, MyRange As Range
Dim LastRow As Long, LastCol As Long
'turn off alerts
Application.DisplayAlerts = False
'set references up-front
Set MySheet = ThisWorkbook.Worksheets("Sheet1")
'identify the last row in column A and the last col in row 1
'then assign a range to contain the full data "block"
With MySheet
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
LastCol = .Range("A" & .Columns.Count).End(xlToLeft).Column
Set MyRange = .Range(.Cells(1, 1), .Cells(LastRow, LastCol))
End With
'apply autofilter to the range showing only dates
'older than january 1st, 2013, then deleting
'all the visible rows except the header
With MyRange
.AutoFilter Field:=1, Criteria1:="<1/1/2013"
.SpecialCells(xlCellTypeVisible).Offset(1, 0).Resize(.Rows.Count).Rows.Delete
End With
'turn off autofilter safely
With MySheet
.AutoFilterMode = False
If .FilterMode = True Then
.ShowAllData
End If
End With
'turn alerts back on
Application.DisplayAlerts = True
End Sub
Running this code on a simple example (on "Sheet1" in this picture) that looks like this:
Will delete all rows with a date older than 1/1/2013, giving you this result:

To answer your question
I am receiving a Next without For error
The problem is you are trying to loop on i but you haven't opened a For i loop. When you indent the code below any code that invokes a Loop or condition (i.e. If) it becomes obvious
Sub DELETEDATE()
Dim x As Long
For x = 1 To Cells.SpecialCells(xlCellTypeLastCell).Row
Debug.Print Cells(x, "A").Value
If CDate(Cells(x, "A")) < CDate("01/01/2013") Then
Cells(i, "A").EntireRow.Delete 'i has no value so Cells(0, "A") is ??
End If
Next x
Next i 'where is the For i = ... in this code?
End Sub
When writing code I try to:
Enter the end command immediately if it's needed. So type If...Then, hit [ENTER], type End If, hit [HOME], hit [ENTER], hit [UP ARROW] then [TAB] to the right place to write the conditional code so that anyone will be able to read and understand it easily.
Always use Option Explicit at the top of every module to force variable declarations.
a tip about deleting rows based on a condition
If you start at the top and work down, every time you delete a row your counter will effectively move to the cell two rows below the row you deleted because the row immediately below the deleted row moves up (i.e. it is not tested at all).
The most efficient way is to loop up from the bottom or your rows:
Sub DELETEDATE()
Dim x As Long
For x = [a1].SpecialCells(xlCellTypeLastCell).Row To 1 Step -1
Debug.Print Cells(x, "A").Value
If CDate(Cells(x, "A")) < CDate("01/01/2013") Then
Cells(x, "A").EntireRow.Delete 'changed i to x
End If
Next x
End Sub
This way, the next row you want to test has been preserved - you've only moved the row below up by 1 and you've tested that row earlier.

Please try with this
Sub DELETEDATE()
Dim x As Long
last = Range("A65536").End(xlUp).Row
For x = 1 To last
Debug.Print Cells(x, "A").Value
check:
If x <= last Then
If Trim(CDate(Cells(x, "A"))) <= Trim(CDate("7/29/2013")) Then
last = last - 1
Cells(x, "A").EntireRow.Delete
GoTo check
End If
End If
Next x
End Sub

You have an additional Next i for some reason in your code as highlighted by the debugger. Try the below:
Sub DELETEDATE()
Dim x As Long
For x = 1 To Cells.SpecialCells(xlCellTypeLastCell).Row
Debug.Print Cells(x, "A").Value
If CDate(Cells(x, "A")) < CDate("01/01/2013") Then
Cells(i, "A").EntireRow.Delete
End If
Next x
End Sub

Related

Looking for specific contents in each of the cells in the column and delete the row in some cases

I'm trying to take the output of our scheduling software for a TV station and get rid of anything for given times. Unfortunately the output of the scheduling software creates a text field for time, not a field that can be formatted to time. I haven't done any real programming in over a decade and this is frustrating me. Here's a sample of the first few rows of the sheet - every day of the month contains entries for each program from 6:00a to the next day at 5:30a.
The code I've got so far is:
Sub delete_extraneous()
Dim rng As Range
Dim j As Integer
Dim m As Integer
m = 1
j = 3
Goto ActiveSheet.Cells(j, m)
With ActiveSheet
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
For m = 1 To lastRow
If rng = "6:30a" Or "7:00a" Or "7:30a" Or "8:00a" Or "8:30a" Or "9:00a" Or "9:30a" Or "10:00a" Or "10:30a" Or "11:00a" Or "11:30a" Then
ActiveCell.EntireRow.Delete Shift:=xlShiftUp
End If
Next m
End Sub
Use an array of text-that-looks-like-time and match against it.
Sub delete_extraneous()
dim tms as variant, lastRow as long
tms = array("6:30a", "7:00a", "7:30a", "8:00a", "8:30a", "9:00a", "9:30a", _
"10:00a", "10:30a", "11:00a", "11:30a")
with activesheet
lastRow = .Cells(.Rows.Count, "C").End(xlUp).Row
For m = lastRow to 1 step-1
If not iserror(application.match(.Cells(m, "C").value, tms, 0)) Then
.rows(m).EntireRow.Delete Shift:=xlShiftUp
End If
Next m
.
end with
end sub
You could use Autofilter():
Sub test()
Dim hours As Variant
hours = Array("6:30a", "7:00a", "7:30a", "8:00a", "8:30a", "9:00a", "9:30a", "10:00a", "10:30a", "11:00a", "11:30a")
With Range("C1", Cells(Rows.Count, 3).End(xlUp))
.AutoFilter Field:=1, Criteria1:=hours, Operator:=xlFilterValues
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
If Not IsError(Application.Match(.Cells(1, 1).value, hours, 0)) Then .Rows(1).Delete
End With
ActiveSheet.AutoFilterMode = False
End Sub
You don't state what your specific issue is in the code, but I can tell you a few problems you have.
1) This is not valid syntax Goto ActiveSheet.Cells(j, m). There is a GoTo statement in VBA, but only use when absolutely necessary. (this case does not require it).
2) Don't rely on ActiveSheet. Instead reference the selected worksheet you desire to work with directly.
3) You never actually define rng so it's meaningless and your code will always bypass range. Using Option Explicit at the top of your modules can help avoid this issue.
4) Using active cell is also dangerous and may produce unintended consequences. In your case it will delete the same cell over and over and over again since you never activate any other cell. It's not needed.
See this code below. It also checks for row deletion and loads into a range for one delete statement later (which will be faster than deleting line by line, and doesn't require backwards looping).
Option Explicit
Sub delete_extraneous()
Dim mySheet As Worksheet
Set mySheet = Worksheets("mySheet") 'replace as needed
Dim lastRow As Long
lastRow = mySheet.Cells(mySheet.Rows.Count, 1).End(xlUp).Row
Dim m As Long
For m = 1 To lastRow
Select Case mySheet.Cells(m, 3).Value 'check each row against column C
Case Is = "6:30a", "7:00a", "7:30a", "8:00a", "8:30a", "9:00a", "9:30a", "10:00a", "10:30a", "11:00a", "11:30a"
Dim deleteRng As Range
If deleteRng Is Nothing Then
Set deleteRng = mySheet.Cells(m, 3)
Else
Set deleteRng = Union(deleteRng, mySheet.Cells(m, 3))
End If
End Select
Next
deleteRng.EntireRow.Delete
End Sub

Need to copy only values from one range into another without overwriting existing data

I've been searching and toying with no luck. I'm trying to copy the values (not formulas) from one range n5:n250 to another m5:m250, but I don't want to overwrite any existing values in m if they exist. ie, if m5 is blank, I want my sub to copy what's in n5 to m5. If it's already got a value, I want it left alone.
This is what I've been trying with no luck:
Sub Reconcile()
Dim i As Long
For i = 5 To 250
If Not IsEmpty(Range("M" & i)) Then _
Range("M" & i) = Range("N" & i)
Next i
Worksheets("Master Task List").Range("e5:e58").ClearContents
End Sub
The worksheets line is the second function I'd like the sub to accomplish when I hit the appropriate button.
I would very much appreciate some help.
Thanks!
You can iterate through your rows in column M, and if the value is equal to vbNullString, set the value to the one in column N.
Sub Reconcile()
Dim i As Long
With ThisWorkbook.Worksheets(1)
For i = 5 To 250
If .Cells(i, "M") = vbNullString Then
.Cells(i, "M") = .Cells(i, "N")
End If
Next
End With
End Sub
This is the routine using IsEmpty. Your example you were using Not IsEmpty. When IsEmpty = True, that means that it's empty. With you placing Not in front you are saying Is Not Empty.
Sub Reconcile()
Dim i As Long
With ThisWorkbook.Worksheets(1)
For i = 5 To 250
If IsEmpty(.Cells(i, "M")) Then
.Cells(i, "M") = .Cells(i, "N")
End If
Next
End With
End Sub
Keep in mind, Worksheets(1) is for illustrative purposes. You may need to change this for your specific worksheet.
You could pick out the blank cells and just move those across in a loop.
I've added an On Error Resume Next around setting the range as I don't know of any other way to trap the error that occurs if there are no blank cells.
Sub Reconcile()
Dim rBlanks As Range
Dim rCell As Range
With ThisWorkbook.Worksheets("Sheet1")
On Error Resume Next
Set rBlanks = .Range("M5:M250").SpecialCells(xlCellTypeBlanks)
On Error GoTo 0
If Not rBlanks Is Nothing Then
For Each rCell In rBlanks
rCell = .Cells(rCell.Row, "N") 'Could also use 14 or rcell.Offset(,1) in place of "N"
Next rCell
Else
MsgBox "No blanks found."
End If
End With
End Sub

Excel - VBA fill in cells between 1st and Last value

I am attempting to use VBA to fill all blank cells in rows with the value to the left, with the exception that I only want to fill the blank cells between the first and last value in the row (not including row 1 and column A, which are identifiers).
I've struggled with getting the loop to stop once the last column with a value has been reached (as this changes with each row), rather than running all the way through the last column on the sheet.
Originally this was marked as duplicate (Autofill when there are blank values), but this does not solve the mentioned problem. This continues until the sheet ends. As seen in the picture below, the fill should stop when the last value is reached.
I am searching for a solution that will allow me to do this for an entire sheet at once, even though the data ends in different columns throughout the sheet. There are 1000+ rows, so running for each row could be quite tedious.
I've been using this code to fill the data (excluding the 1st row and column). But this is where I am not sure how to get it to stop at the last value in the row.
Sub test()
With ThisWorkbook.Sheets("Sheet1").Range("A:A")
With Range(.Cells(2, 2), .Cells(.Rows.Count, 36).End(xlUp))
With .Offset(0, 1)
.Value = .Value
On Error Resume Next
.SpecialCells(xlCellTypeBlanks).FormulaR1C1 = "=RC[-1]&"""""
On Error GoTo 0
.Value = .Value
End With
End With
End With
End Sub
If my explanation was not clear, This is a sample and the output I am trying to create
Thank you all so much in advance for all your help!
You may try something like this...
Sub FillBlanks()
Dim r As Long, lr As Long, lc As Long
Dim cell As Range, FirstCell As Range, LastCell As Range
lr = Cells(Rows.Count, 1).End(xlUp).Row
lc = Cells(2, Columns.Count).End(xlToLeft).Column
For r = 3 To lr
Set FirstCell = Range(Cells(r, 1), Cells(r, lc)).Find(what:="*", after:=Cells(r, 1))
If Not FirstCell Is Nothing And FirstCell.Column > 1 Then
Set LastCell = Cells(r, Columns.Count).End(xlToLeft)
Range(FirstCell, LastCell).SpecialCells(xlCellTypeBlanks).FormulaR1C1 = "=RC[-1]"
Range(FirstCell, LastCell).Value = Range(FirstCell, LastCell).Value
End If
Next r
End Sub
And here is yet another solution (just to give you some variety):
Option Explicit
Sub fillInTheBlanks()
Dim lngRow As Long
Dim ws As Worksheet
Dim lngColumn As Long
Dim bolStart As Boolean
Dim lngLastColumn As Long
Dim dblTempValue As Double
Dim arrSheetCopy As Variant
Set ws = ThisWorkbook.Worksheets("Sheet1")
arrSheetCopy = ws.Range(ws.Cells(3, 1), ws.Cells(ws.Cells(ws.Rows.Count, "A").End(xlUp).Row, ws.UsedRange.Columns.Count)).Value2
For lngRow = LBound(arrSheetCopy, 1) To UBound(arrSheetCopy, 1)
bolStart = False
lngLastColumn = 0
For lngColumn = LBound(arrSheetCopy, 2) To UBound(arrSheetCopy, 2)
If Not arrSheetCopy(lngRow, lngColumn) = vbEmpty Then lngLastColumn = lngColumn
Next lngColumn
For lngColumn = LBound(arrSheetCopy, 2) To lngLastColumn
If arrSheetCopy(lngRow, lngColumn) = vbEmpty And bolStart Then
arrSheetCopy(lngRow, lngColumn) = dblTempValue
Else
If Not arrSheetCopy(lngRow, lngColumn) = vbEmpty And IsNumeric(arrSheetCopy(lngRow, lngColumn)) Then
bolStart = True
dblTempValue = CDbl(arrSheetCopy(lngRow, lngColumn))
End If
End If
Next lngColumn
Next lngRow
ws.Range("A3").Resize(UBound(arrSheetCopy, 1), UBound(arrSheetCopy, 2)).Value2 = arrSheetCopy
End Sub
This one is probably the fastest solution (even though it seems a bit bulky with much more lines of code when compared to the other solutions). That's due to the fact that this solution is doing most of the work in memory and not on the sheet. The entire sheet is loaded into a variable and then the work is done on the variable before the result (the variable) is written back to the sheet. So, if you have a speed problem then you might want to consider using this solution.
Here is one possible that meets your sample data's expectations.
Sub wqewqwew()
Dim i As Long, fc As Variant, lc As Long
'necessary if you do not want to confirm numbers and blanks in any row
On Error Resume Next
With ThisWorkbook.Worksheets("Sheet6")
For i = 3 To .Cells(.Rows.Count, "A").End(xlUp).Row
If CBool(Application.Count(Rows(i))) Then
fc = Intersect(.Rows(i), .UsedRange).Offset(0, 1).SpecialCells(xlCellTypeConstants, xlNumbers).Cells(1).Column
If Not IsError(fc) Then
lc = Application.Match(9 ^ 99, .Rows(i))
On Error Resume Next
With .Range(.Cells(i, fc), .Cells(i, lc))
.SpecialCells(xlCellTypeBlanks).Cells.FormulaR1C1 = "=RC[-1]"
.Value = .Value2
End With
End If
End If
Next i
End With
End Sub
Just another solution:
The following code can help is there you need to auto-fill the previous values between 1st and last cells depending on value of 1st cell as mentioned in question Excel - VBA fill in cells between 1st and Last value
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim i As Long
For i = 2 To Target.Column
If Cells(Target.Row, i) = "" Then
If Cells(Target.Row, i - 1) <> "" Then
Range(Cells(Target.Row, i), Cells(Target.Row, i)).Value = Range(Cells(Target.Row, i - 1), Cells(Target.Row, i - 1)).Value
End If
End If
Next i
End Sub
This sub is activated by clicking on any cell. Same cell marks the end of the loop i.e. to stop the loop just click the cell till which you want to fill the blank cells.

Alternatives to using the .Select property in a Do Until loop

I'm trying to run a simple nested If & Do Until loop for multiple sheets:
Sub DoUntil()
Dim ws As Worksheet
Dim i As Integer
For Each ws In ActiveWorkbook.Worksheets
For i = 1 To 25
If ws.Cells(i, 10) = "T" Then
ws.Cells(i, 10).Select
Do Until ActiveCell = "P"
ActiveCell.Offset(1, 1) = 1
ActiveCell.Offset(1, 0).Select
Loop
End If
Next i
Next ws
End Sub
PURPOSE: This function is meant to loop through a column, and place 1's in the next column to the right. The 1's must start just after "T" and end at "P". The T's and P's are unevenly spaced.
PROBLEM: Running this code for a single worksheet without the ws. object gives no problem. However, as soon as I want to loop through multiple worksheets, the .Select fails to fetch the specified range (Error 1004). This error is given at
ws.Cells(i, 10).Select
QUESTION: I know the perils of using the .Select property, but I'm unsure of how to tackle this problem differently as the ActiveCell is crucial for my Do Until function. Is there another way to run my Do Until function that will work with the If statement and loop through multiple sheets?
Try the below:
Sub DoUntil()
Dim ws As Worksheet
Dim i As Integer, j as Integer
For Each ws In ActiveWorkbook.Worksheets
For i = 1 To 25
If ws.Cells(i, 10) = "T" Then
j = i
Do Until ws.Cells(j,10) = "P"
ws.Cells(j, 10+1) = 1
j = j + 1
Loop
End If
Next i
Next ws
End Sub
use match, something like
cells(match("T",range("a1:a50000"),0),1) to
cells(match("P",range("a" & match("T",range("a1:a50000"),0) & ":a50000"),0),1)
and something like
range(cells(y,x),cells(y2,x2)).offset(0,1).value=1

VBA get rid of full row if one specific column repeats a word in next row

I am trying to write a vba code to get rid of a row if any word is immediately repeated in same column (column E) but in other row. If that happens, the row to be deleted is the one more on top. Follow an example below. In this case, the row to be dropped are: E6, E10 and E15.
Name of the sheet is test. Columns and F and G are not relevant.
Thanks a lot!
Edit to add code from comments:
Sub delete_duplicates_column_E()
With Sheets("test").Range("A:E")
.Value = .Value
.RemoveDuplicates Columns:=5, Header:=xlYes
End With
End Sub
Just whipped this up, try it:
Sub removeDuplicates()
Range("E2").Select
Do Until ActiveCell.Value = ""
If ActiveCell.Value = ActiveCell.Offset(1).Value Then
ActiveCell.EntireRow.Delete (xlShiftUp)
End If
ActiveCell.Offset(1).Select
Loop
End Sub
When deleting rows it is better to loop back wards:
Sub delete_duplicates_column_E()
Dim ws As Worksheet
Dim lastrow As Long
Dim i As Long
Set ws = Sheets("test")
With ws
lastrow = .Range("E" & .Rows.Count).End(xlUp).Row
For i = lastrow To 2 Step -1
If .Cells(i, 5) = .Cells(i + 1, 5) Then
.Rows(i).Delete
End If
Next i
End With
End Sub