Issues with a VBA For Next loop not working - excel

I am trying to wrap my head around a piece of code that has worked for me in the past not working right now in a different application. Basically it needs to delete all rows that contain information in a specific column and leave the rest untouched. When I run the macro the code executes without issues, but just deletes some of the rows with values, not all of them. When running the code several times in a row it ends up doing what it is intended, but it's really not convenient. Here is the code:
Sub Delete_Signoffed()
Dim rCell As Range
Dim iCol As Integer
Dim iRow As Integer
Worksheets("MilestoneDueDate").Activate
If ActiveSheet.AutoFilterMode Then Cells.AutoFilter
ActiveWindow.FreezePanes = False
Columns.EntireColumn.Hidden = False
If WorksheetFunction.CountA(Columns("A")) = 0 Then
Columns("A").Delete
Rows("1:6").Delete
End If
iCol = Cells.Find("Sign-Off By", LookAt:=xlWhole).Column
For iRow = 2 To Cells(Rows.Count, iCol).End(xlUp).Row
Cells(iRow, iCol).Select
If Not IsEmpty(Cells(iRow, iCol).Value) Then Rows(iRow).EntireRow.Delete
Next iRow
End Sub
The source file has some formatting issues, everything that comes before assigning iCol the column value is to fix the format, so please ignore. iRow starts at 2 to avoid deleting the file headers.
Any ideas on why the For loop is not working as intended?
Thanks in advance!

I updated your script. I also added comments for you to be able to understand it better and able to improve it in the future.
Sub Delete_Signoffed()
'Goto CleanUp if there are errors
On Error GoTo CleanUp
Dim wsMilestoneDueDate As Worksheet
Dim rCell As Range
Dim iCol As Integer
Dim iRow As Integer
Set wsMilestoneDueDate = ActiveWorkbook.Worksheets("MilestoneDueDate")
'Disable temporarily Screen Updating
Application.ScreenUpdating = False
With wsMilestoneDueDate
.Activate 'No need, but if you prefer you can
'Activate Auto Filter
If .AutoFilterMode Then Cells.AutoFilter
'Remove FreezePanes
ActiveWindow.FreezePanes = False
'Unhide Columns
.Columns.EntireColumn.Hidden = False
'Delete Empty Columns/Rows if they are all empty
If WorksheetFunction.CountA(.Columns("A")) = 0 Then
Columns("A").Delete
Rows("1:6").Delete
End If
'Get the last Column
iCol = .UsedRange.Find("Sign-Off By", LookAt:=xlWhole).Column
'Start Deleting but from the last to the first (Backward)
For iRow = Cells(Rows.Count, iCol).End(xlUp).Row To 2 Step -1
Set rCell = Cells(iRow, iCol)
'Delete the entire row if it is NOT empty
If Not IsEmpty(rCell.Value) Then
'Deletion
Rows(iRow).EntireRow.Delete
End If
Next iRow
End With
CleanUp:
'Purge Memory
Set wsMilestoneDueDate = Nothing
Set rCell = Nothing
'Restore Screen Updating
Application.ScreenUpdating = True
End Sub

As already stated in comments, the flaw in your code was not looping backwards
But I hereby give you a solution without looping and using one line only, thanks to SpecialCells method of Range object, specifying it to filter cells with some "constant" (i.e. not deriving from formulas) value
Range(Cells(2, iCol), Cells(Rows.Count, iCol).End(xlUp)).SpecialCells(xlCellTypeConstants).EntireRow.Delete
this assumes you always have at least one value below row 1
should not that be the case then just add a check:
If Cells(Rows.Count, iCol).End(xlUp).Row > 1 Then Range(Cells(2, iCol), Cells(Rows.Count, iCol).End(xlUp)).SpecialCells(xlCellTypeConstants).EntireRow.Delete
While looking at your entire code you should adopt the good practice to avoid Select/Selection, Activate/ActiveXXX pattern and Always fully qualify ranges up to their worksheet (if not workbook) parent objects, like follows:
Sub Delete_Signoffed()
Dim iCol As Long
With Worksheets("MilestoneDueDate") ' reference wanted sheet
If .AutoFilterMode Then .Cells.AutoFilter
ActiveWindow.FreezePanes = False
.Columns.EntireColumn.Hidden = False
If WorksheetFunction.CountA(.Columns("A")) = 0 Then
.Columns("A").Delete
.Rows("1:6").Delete
End If
iCol = .Range("A1", .Cells(1, .Columns.Count).End(xlToLeft)).Find("Sign-Off By", LookAt:=xlWhole, LookIn:=xlValues).Column
.Range(.Cells(2, iCol), .Cells(.Rows.Count, iCol).End(xlUp)).SpecialCells(xlCellTypeConstants).EntireRow.Delete
End With
End Sub
as you can see
all range objects (Columns(), Rows, Range, Cells) are referencing Worksheets("MilestoneDueDate") through that dot (.) in front of them
iCol is being set running Find() method in the most restricted range possibile

Related

AutoFilter Function is Deleting my Column Headers

I run my VBA code and the first time it runs I get the result I want but if I run it a second time my column headers get deleted. FYI my table starts on E and goes through N. My button is on column O and also gets deleted when I run it a second time.
Switching the Range did not help and setting AutoFilter to false also did not work.
#
Sub Auto_filter()
Dim sh As Worksheet
Set sh = ThisWorkbook.Worksheets("HP Service Manager")
sh.AutoFilterMode = False
With sh
On Error Resume Next
.ShowAllData
.Range("E1:N1").AutoFilter 1, "IM*"
AutoFilter = False
End With
End Sub
#
Expect to not have column headers deleted.
The issue is in the second code you shared.
The code is first setting the range here:
Set Rng = Range("E1", Cells(iRow, "E"))
And then here it is trying to delete all visible cells in the range (after applying the filter)
Rng.SpecialCells(xlCellTypeVisible).EntireRow.Delete
which also includes your header cell.
So, a simple way to deal with it could be to set another range like this
Set Rng2 = Range("E2", Cells(iRow, "E"))
and then using it to delete the data
Rng2.SpecialCells(xlCellTypeVisible).EntireRow.Delete
Here is the re-written function for your reference. This only deletes the rows starting from row 2 that are blank. You may want to add some error handing in case there are no blank rows to delete etc.
Sub DeleteRowsAll()
Dim iRow As Long
Dim Rng As Range
Application.ScreenUpdating = False
Rows(1).Insert
Range("E1").Value = "rabbitohs"
With ActiveSheet
.UsedRange
iRow = .Cells.SpecialCells(xlCellTypeLastCell).Row
Set Rng = Range("E1", Cells(iRow, "E"))
Rng.AutoFilter Field:=1, Criteria1:=""
Set Rng2 = Range("E2", Cells(iRow, "E"))
Rng2.SpecialCells(xlCellTypeVisible).EntireRow.Delete
.UsedRange
End With
End Sub
Sub DeleteRowsAll()
Dim LastRow As Long
Dim CellValue As String
LastRow = Worksheets("HP Service Manager").Cells(Rows.Count, "E").End(xlUp).Row
For i = LastRow To 2 Step -1
CellValue = Worksheets("HP Service Manager").Cells(i, "E").Value
If CellValue = "" Then
Worksheets("HP Service Manager").Rows(i).Delete
End If
Next i
End Sub

When a specific word is found, delete that row AND the 2 rows under. Is this possible with VBA?

As the photo shows, all my data is on ONE column. The "trigger word" is "Past Car" and want that entire row PLUS the two rows under it deleted.
So according to the photo below rows 5,6,7 and 18,19,20 and 26,27,28 would be deleted.
Is this possible with VBA? I've tried using search functions and some VBA techniques but got overwhelmed.
Screenshot
I would suggest to do it like that
Option Explicit
Sub DelIt()
Const PAST_CAR = "Past Car"
Const OFF_SET = 3
Dim lastRow As Long, i As Long
Dim ws As Worksheet
Dim deleteRange As Range
Set ws = ActiveSheet
lastRow = ws.Cells(Rows.Count, "A").End(xlUp).Row
With ws
For i = 1 To lastRow
If .Cells(i, 1).Value = PAST_CAR Then
If deleteRange Is Nothing Then
Set deleteRange = .Rows(i).Resize(OFF_SET)
Else
Set deleteRange = Union(deleteRange, .Rows(i).Resize(OFF_SET))
End If
End If
Next i
End With
If Not (deleteRange Is Nothing) Then
deleteRange.EntireRow.Delete
End If
End Sub
In this way you do not need to loop backwards or turn of ScreenUpdatings as you only have one "write" access to the sheet.
You must loop through the cells in column A in reverse order and check if the cell content is Past Car and if so, delete the rows accordingly.
You may try something like this...
Sub DeleteRows()
Dim lr As Long, i As Long
Application.ScreenUpdating = False
lr = Cells(Rows.Count, "A").End(xlUp).Row
For i = lr To 1 Step -1
If Cells(i, 1) = "Past Car" Then
Range("A" & i).Resize(3).EntireRow.Delete
End If
Next i
Application.ScreenUpdating = True
End Sub

VBA code for clearing last cell and formulas from last cell

I want to clear the whole cell including formulas from the last blank cell. Here is what i'm trying to work with but its not working. Would other code affect it? Thanks
Dim myLastRow As Long
Dim clearCell As Long
Application.ScreenUpdating = False
' Find last row
myLastRow = Cells(Rows.Count, "C").End(xlUp).Row
' Loop through range
For clearCell = 4 To myLastRow
If Cells(clearCell, "C").Value = "" Then Range(Cells(clearCell, "C"), Cells(clearCell, "C")).Clear
Next clearCell
Application.ScreenUpdating = True
I don't see anything that would cause an error in your code, other than the fact that your references aren't qualified with the sheet that is being used - which means everything will default to operating on ActiveSheet which may not be the sheet you are wanting it to work on.
Assuming the sheet you want to process has a Name of "Stow", the following code should be safer:
Dim myLastRow As Long
Dim clearCell As Long
Application.ScreenUpdating = False
'Use a "With" block to save typing Worksheets("Stow") in lots of places
'(e.g. within the block we can type ".Cells" instead of "Worksheets("Stow").Cells" and
' ".Rows" instead of "Worksheets("Stow").Rows")
With Worksheets("Stow")
' Find last row
myLastRow = .Cells(.Rows.Count, "C").End(xlUp).Row
' Loop through range
For clearCell = 4 To myLastRow
''Clear any cells with a value of ""
'If .Cells(clearCell, "C").Value = "" Then .Cells(clearCell, "C").Clear
'Clear any cells with a value of "-"
If .Cells(clearCell, "C").Value = "-" Then .Cells(clearCell, "C").Clear
Next clearCell
End With
Application.ScreenUpdating = True

Copy rows in Excel if cell contains name from an array

I have an Excel sheet that contains entries for ~150 employees. Each row contains the name as well as hours worked, pay, team, etc etc etc etc. The B column in each row contains the employees name in Last,First format. About half the employees on the sheet are part time employees. What i'm trying to do is write a macro in VB that copies the entire row if the name in the B column matches one of the names of the part time employees so that one of my coworkers can simply run the macro and paste all of the rows of copied users into a new sheet each week. Here's what I currently have. (I have all of the employees names in the array however I have censored them out) I really don't understand much of the last 50% of the code. This stuff was stuff I found online and have been messing around with.
`Sub PartTime()
Dim strArray As Variant
Dim wsSource As Worksheet
Dim wsDest As Worksheet
Dim NoRows As Long
Dim DestNoRows As Long
Dim I As Long
Dim J As Integer
Dim rngCells As Range
Dim rngFind As Range
Dim Found As Boolean
nameArray = Array(NAMES CENSORED)
Set wsSource = ActiveSheet
NoRows = wsSource.Range("A65536").End(xlUp).Row
DestNoRows = 1
Set wsDest = ActiveWorkbook.Worksheets.Add
For I = 1 To NoRows
Set rngCells = wsSource.Range("C" & I & ":F" & I)
Found = False
For J = 0 To UBound(strArray)
Found = Found Or Not (rngCells.Find(strArray(J)) Is Nothing)
Next J
If Found Then
rngCells.EntireRow.Copy wsDest.Range("A" & DestNoRows)
DestNoRows = DestNoRows + 1
End If
Next I
End Sub`
This code should work for what you are looking for. It is important to note that the string names in your array must be identical to that in Column B (with the exception of leading and trailing spaces), so if the names are written "LastName, FirstName" then your input data must be identical. This code could be tweaked to not have this requirement, but for now I've left it as such. Let me know if you'd prefer the code be adjusted.
Option Explicit
Sub PartTimeEmployees()
Dim NewSheet As Worksheet, CurrentSheet As Worksheet, NameArray As Variant
Set CurrentSheet = ActiveWorkbook.ActiveSheet
Set NewSheet = Sheets.Add(After:=Sheets(Worksheets.Count))
NewSheet.Name = "Part Time Employees"
NameArray = Array("NAMES CENSORED")
'Pulling headers from the first row
CurrentSheet.Rows(1).EntireRow.Copy
NewSheet.Select 'Redundant but helps avoid the occasional error
NewSheet.Cells(1, 1).Select
ActiveSheet.Paste
CurrentSheet.Select
Dim NextRow As Long
NextRow = 2
'Writing this code to not assume that the data is continuous
Dim Count As Long
'Iterating to the end of the data in the sheet
For Count = 2 To CurrentSheet.UsedRange.Rows.Count
If Not IsEmpty(CurrentSheet.Cells(Count, 2)) Then
For Counter = 1 To UBound(NameArray)
'Performing string operations on the text will be faster than the find method
'It is also essential that the names are entered identically in your array
If UCase(Trim(CurrentSheet.Cells(Count, 2).Value)) = UCase(NameArray(Counter)) Then
CurrentSheet.Rows(Count).Copy
NewSheet.Select
NewSheet.Cells(NextRow, 1).Select
ActiveSheet.Paste
CurrentSheet.Select
NextRow = NextRow + 1
Exit For
End If
Next Counter
End If
Next Count
End Sub
No need to loop through the array if you use a Range.AutoFilter Method with the array as criteria.
See comment for each line of operational code.
Option Explicit
Sub partTimers()
Dim nameArray As Variant
'construct an array of the part-time employees' names
nameArray = Array("Trgh, Evtfk", "Mtre, Sdnrm", _
"Sfgd, Pxduj", "Lsds, Qwrml", _
"Eqrd, Oqtts")
With Worksheets("Sheet1") 'you should know what worksheet the names are on
'turn off AutoFilter is there is one already in operation
If .AutoFilterMode Then .AutoFilterMode = False
'use the 'island' of cells radiating out from A1
With .Cells(1, 1).CurrentRegion
'apply AutoFilter using array of names as criteria
.AutoFilter field:=2, Criteria1:=nameArray, Operator:=xlFilterValues
'check if there is anything to copy
If Application.Subtotal(103, .Columns(2)) > 1 Then
'copy the filtered range
.Cells.Copy
'create a new worksheet
With .Parent.Parent.Worksheets.Add(After:=Sheets(Sheets.Count))
'paste the filtered range, column widths and cell formats
.Cells(1, 1).PasteSpecial Paste:=xlPasteColumnWidths
.Cells(1, 1).PasteSpecial Paste:=xlPasteFormats
.Cells(1, 1).PasteSpecial Paste:=xlPasteValues
End With
End If
End With
'turn off the AutoFilter
If .AutoFilterMode Then .AutoFilterMode = False
'turn off active copy range
Application.CutCopyMode = False
End With
End Sub

Moving rows based on column values

I need to scan through all of the rows in the "Master" worksheet, find any cells with the value "Shipped" in the column "Status", then cut and paste each entire row to another sheet. The pasted rows need to be placed after the last row also.
I found this post (pasted below) which I slightly modified to delete rows successfully. But I can not figure out how to move rows instead. Should I try an entirely new method?
Sub DeleteRows()
Dim rng As Range
Dim counter As Long, numRows as long
With ActiveSheet
Set rng = Application.Intersect(.UsedRange, .Range("C:C"))
End With
numRows = rng.Rows.Count
For counter = numRows to 1 Step -1
If Not rng.Cells(counter) Like "AA*" Then
rng.Cells(counter).EntireRow.Delete
End If
Next
End Sub
I do not know VBA. I only kind of understand it because of my brief programming history. I hope that is okay and thank you for any help.
There's a couple of ways you could do it, can you add a filter to the top columns, filter by the value of 'Shipped'? Does it need to be copy and pasted into a new sheet?
It's not the most concise code but it might work
sub Shipped_filter()
dim wsSheet as worksheet
dim wsOutputSheet as worksheet
dim BottomRow as integer
Set wsSheet = worksheets("Sheet1") 'change to the sheet name
set wsOutputSheet = worksheets("Sheet2") 'change to the sheet name
'*****************************
'* Delete old data on Sheet2 *
'*****************************
wsoutputsheet.activate
Activesheet.cells.clearall
wsSheet.range("A1").select
selection.autofilter
BottomRow = wsSheet.range("A90000").end(xlup).row ' or another column you guarantee will always have a value
activesheet.range("$A$1:$Z$"&BottomRow).AutoFilter field:=1, Criteria1:="Shipped" ' change field to whatever column number Status is in
'********************************
'* Error trap in case no update *
'********************************
if activesheet.range("A90000").end(xlup).row = 1 then
msgbox("Nothing to ship")
exit sub
end if
wsSheet.range("A1:Z"&Bottomrow).select
selection.copy
wsOutputSheet.range("A1").select
selection.pastespecial Paste:=xlpastevalues
application.cutcopymode = false
msgbox('update complete')
end sub
I haven't tried it so it might need updating
I ended up combining the code I was originally using (found here) with an AutoFilter macro (found here). This is probably not the most efficient way but it works for now. If anyone knows how I can use only the For Loop or only the AutoFilter method that would be great. Here is my code. Any edits I should make?
Sub DeleteShipped()
Dim lastrow As Long
Dim rng As Range
Dim counter As Long, numRows As Long
With Sheets("Master")
'Check for any rows with shipped
If .Range("R:R").Find("Shipped", , xlValues, xlWhole, , , False) Is Nothing Then
MsgBox "No shipped plates found. ", , "No Rows Moved": Exit Sub
Else
Application.ScreenUpdating = False
'Copy and paste rows
lastrow = .Range("A" & Rows.Count).End(xlUp).Row
lastrow2 = Worksheets("ShippedBackup").Cells(Rows.Count, "A").End(xlUp).Row + 1
.Range("A1:U" & lastrow).AutoFilter field:=18, Criteria1:="Shipped"
.Range("A2:U" & lastrow).SpecialCells(xlCellTypeVisible).EntireRow.Copy
Sheets("ShippedBackup").Range("A" & lastrow2).PasteSpecial xlPasteValues, xlPasteSpecialOperationNone, False, False
.ShowAllData
'Delete rows with shipped status
Set rng = Application.Intersect(.UsedRange, .Range("R:R"))
numRows = rng.Rows.Count
For counter = numRows To 1 Step -1
If rng.Cells(counter) Like "Shipped" Then
rng.Cells(counter).EntireRow.Delete
End If
Next
MsgBox "All shipped records have been moved to the ""ShippedBackup"" worksheet.", , "Backup Complete"
End If
End With
Hope it helps someone!

Resources