I'm creating a loop that is supposed to find cells that have dates before 01/01/2019 and if they find they delete the entire row.
The problem is that if they delete per example row number 2, the code is passing to row 3, but since row 2 was deleted previously the original row 3 would be skipped.
I wrote the loop and tried to add "i = 1 - 1" but the excel never stops running the code and blocks the program
Dim ws As Worksheet
Dim Ldate As Date
Dim N As Long
Ldate = "01/01/2019"
N = ws.Cells(Rows.Count, 8).End(xlUp).Offset(0, 0).Row
For i = 1 To N
If ws.Cells(i, 8).Value < Ldate Then
ws.Rows(i).Delete
' i = 1 - 1
End If
Next i
Don't let Excel guess if this string "01/01/2019" is mm/dd/yyyy or dd/mm/yyyy Never use strings to write a date, instead always use real dates: Ldate = DateSerial(2019, 1, 1) to create dates. String-Dates are very evil! The only use case to cast a real date into a string is to print it on paper or screen. All other cases must use real dates (number formats) to be reliable.
.Offset(0, 0) is usless remove it. Moving 0 rows and 0 columns from current cell will not change current cell at all.
If you delete/add rows then your loops must be backwards in order to row count correctly: For iRow = LastRow To 1 Step - 1
So you end up with something like this
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim Ldate As Date
Ldate = DateSerial(2019, 1, 1)
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, 8).End(xlUp).Row
Dim iRow As Long
For iRow = LastRow To 1 Step - 1
If ws.Cells(iRow , 8).Value < Ldate Then
ws.Rows(iRow).Delete
End If
Next iRow
Alternative if you use a forward loop, you need to collect the rows to delete in a Range variable and delete it at once after the loop is finished. This way deleting doesn't affect the row counting inside the loop (because we delete after the loop):
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim Ldate As Date
Ldate = DateSerial(2019, 1, 1)
Dim LastRow As Long
LastRow = ws.Cells(ws.Rows.Count, 8).End(xlUp).Row
Dim RowsToDelete As Range
Dim iRow As Long
For iRow = 1 To LastRow
If ws.Cells(iRow , 8).Value < Ldate Then
If RowsToDelete Is Nothing Then
Set RowsToDelete = ws.Rows(iRow)
Else
Set RowsToDelete = Union(RowsToDelete, ws.Rows(iRow))
End If
End If
Next iRow
If Not RowsToDelete Is Nothing Then
RowsToDelete.Delete
End If
Actually this approach should be faster because it only performs one delete action in the end (instead of one per deleted row).
Edit according comments:
Test if a worksheet exists before you use it.
Option Explicit
Function WorksheetExists(ByVal WorksheetName As String, Optional ByVal InWorkbook As Workbook) As Boolean
'default workbook is ThisWorkbook
If InWorkbook Is Nothing Then
Set InWorkbook = ThisWorkbook
End If
Dim ws As Worksheet
On Error Resume Next
Set ws = InWorkbook.Worksheets(WorksheetName)
On Error GoTo 0
WorksheetExists = Not ws Is Nothing
End Function
Then use it like
If WorksheetExists("Sheet1") Then …
'or to test for a worksheet in a different workbook
If WorksheetExists("Sheet1", Workbooks("OtherWorkbook.xlsx")) Then
The simplest way is to change
For i = 1 To N
to
For i = N To 1 step -1
This way when your row is deleted it will not change the number of the following rows as these will be lower numbered rows.
Related
In my vba code below I am trying to delete to specific colors from a row. Right now I would like to combine 2 if statements into 1 if statement. Right now my code below is working but is inefficient if more colors are added. Look for the if statements regarding blue and red for this problem.
Sub collapse_columns()
Dim x As Integer
For x = 1 To 4
collapse_column x
Next
End Sub
Sub collapse_column(column_number As Integer)
Dim row As Long
Dim s As Worksheet
Dim last_row As Long
Set s = ActiveSheet ' work on the active sheet
'Set s = Worksheets("Sheet1") 'work on a specific sheet
last_row = ActiveSheet.Cells(s.Rows.Count, column_number).End(xlUp).row
For row = last_row To 1 Step -1
If Cells(row, column_number).Value = "red" Then Cells(row, column_number).Delete xlUp
Next
For row = last_row To 1 Step -1
If Cells(row, column_number).Value = "blue" Then Cells(row, column_number).Delete xlUp
Next
End Sub
When I have many possible values that can trigger the same code, I like to use a string to hold the values, then search the string to find a match as follows:
Sub collapse_column(column_number As Integer)
Dim row As Long
Dim s As Worksheet
Dim last_row As Long
Set s = ActiveSheet ' work on the active sheet
'Set s = Worksheets("Sheet1") 'work on a specific sheet
last_row = ActiveSheet.Cells(s.Rows.Count, column_number).End(xlUp).row
Dim colors_to_delete As String
colors_to_delete = ",red,blue," ' be sure to keep the leading and trailing commas
For row = last_row To 1 Step -1
If InStr(1, colors_to_delete, "," & Cells(row, column_number).Value & ",") > 0 Then Cells(row, column_number).Delete xlUp
Next
End Sub
I'm trying to loop through my data and Union certain row numbers that I need to delete later on. The code below stores the correct rows, but I can't delete them. I believe it's because my data is arranged in a table, since I'm able to delete the desired rows if the data is not in a table. I get the error message 'run time error 1004 - delete method of range class failed' on the line Urng.delete.
Sub DeleteRows()
Dim ws4 As Worksheet: Set ws4 = Worksheets("Sheet1")
Dim LastRow As Long
Dim CurrentRow As Long
Dim GroupValue
Dim GroupTotal As Long
Dim x As Long
Dim Urng As Range
Application.ScreenUpdating = False
ws4.Activate
GroupValue = ws4.Range("B6").Value
CurrentRow = 6 LastRow = ws4.Cells(Rows.Count, "B").End(xlUp).Row
Set Urng = Rows(LastRow + 1)
For x = 1 To LastRow
GroupTotal = Application.WorksheetFunction.CountIf(Range("B6:B" & LastRow), GroupValue)
If GroupTotal = 1 Then
Set Urng = Union(Urng, Rows(CurrentRow))
End If
CurrentRow = CurrentRow + GroupTotal
GroupValue = Range("B" & CurrentRow).Value
If GroupValue = "" Then '
Exit For
End If
Next x
Urng.Delete
Application.ScreenUpdating = True
End Sub
I've tried using .EntireRow.Delete without luck.
There's no data outside the table, so deleting just the table rows could be a solution, however, I don't know how to build the loop that Unions the row numbers if I can't use the row number in Union(Urng, Rows(CurrentRow)).
Is there a VBA-solution to delete multiple entire rows, where part of the row is a table?
This is how to delete row number 5 from a table named TableName:
Sub TestMe()
Range("TableName[#All]").ListObject.ListRows(5).Delete
End Sub
Concerning your specific problem, the case is that in Urng you are having rows, which are both in and outside the table. Thus, they cannot be deleted with .Delete. Write this before Urng.Delete to see yourself:
Urng.Select
Stop
Unrg.Delete
At the sample you may see that the row 6 is in the table and row 18 is outside the table:
Concerning deletion of two rows, which are not close to each other in a table, I guess that the only way is to loop. It is a bit slower indeed, but it works:
Sub TestMe()
Dim cnt As Long
Dim arrRows As Variant: arrRows = Array(10, 12)
Dim table As ListObject: Set table = ActiveSheet.ListObjects("SomeTable")
For cnt = UBound(arrRows) To LBound(arrRows) Step -1
table.ListRows(arrRows(cnt)).Delete
Next cnt
'This works only when the rows are after each other, e.g. 2,3,4
table.Range.Rows("2:4").Select
Stop
table.Range.Rows("2:4").Delete
End Sub
I am trying to delete all row where column A value(Its formatted as date) is less than today's date. I have to run these through entire non empty A column. but facing an issue with the code to run as loop through entire rows. each time its deleting only 1 row. Please let me know how to run it through entire row set.
Sub DeleteRowBasedOnDateRange()
Dim spem As Workbook
Dim ws As Worksheet
Dim N As Long, I As Long
Set spem = Excel.Workbooks("SwitchP.xlsm")
Set ws = spem.Worksheets("data")
N = ws.Cells(Rows.count, "A").End(xlUp).row
For I = 2 To N
If Cells(I, "A").Value < Date Then
Cells(I, "A").EntireRow.Delete
I = I + 1
End If
Next I
End Sub
Quick fix
Loop backwards.
Also you do not need the I=I+1 as that is done automatically.
Sub DeleteRowBasedOnDateRange()
Dim spem As Workbook
Dim ws As Worksheet
Dim N As Long, I As Long
Set spem = Excel.Workbooks("SwitchP.xlsm")
Set ws = spem.Worksheets("data")
N = ws.Cells(ws.Rows.count, "A").End(xlUp).row
For I = N to 2 Step -1
If ws.Cells(I, "A").Value < Date Then
ws.Rows(I).Delete
End If
Next I
End Sub
I have a workbook with 10 sheets. Each sheet has about 30,000 rows with URL. I have a hand full of URLs (about 10 different URLs) that I need to keep the data. Is there a way to delete all the rows from all the worksheet if the first column (Column A - URL) does not contain one of the URL.
for example, I would like to keep we.abc.us, ss.boli.us and 3m.mark.us and delete rest of the rows from all the worksheet in the workbook.
Sub delete0rows()
Dim Worksheet As Excel.Worksheet
Dim lastRow As Long
Dim i As Integer
For Each Worksheet In Application.ThisWorkbook.Worksheets
lastRow = Worksheet.Cells(Rows.Count, 1).End(xlUp).Row
i = 1
Do While i <= lastRow
If Worksheet.Range("A" & i).Value = 0 Then
Worksheet.Rows(i).Delete i = i - 1
lastRow = lastRow - 1
End
i = i + 1
Loop
Next Worksheet
End Sub
I suggest you introduce reverse For loop using Step -1:
Sub delete0rows()
Dim Worksheet As Excel.Worksheet
Dim lastRow As Long
Dim i As Integer
For Each Worksheet In Application.ThisWorkbook.Worksheets
lastRow = Worksheet.Cells(Rows.Count, 1).End(xlUp).Row
For i = lastRow To 1 Step -1
If Worksheet.Range("A" & i).Value = 0 Then
Worksheet.Rows(i).EntireRow.Delete
End If
Next i
Next Worksheet
End Sub
I found this sub a while back. I cannot remember who the original author was or I would credit them. I did tweak it slightly to pass variables into it
The nice thing about this is you can pass multiple deletion criteria by passing a space separated string
Essentially you can give it a row to start at (in case you have headers) tell it the column to look in, the sheet that column is on and your criteria/criterion. So for example if I want it to start at row 5 checking each row below that on a sheet named 'cleanup' checking column 'D' for the words 'cat' 'dog' and 'fish' I would write
Call DelRow(5,"D","cleanup","cat dog fish")
Public Sub DelRow(DataStartRow As Long, SearchColumn As String, SheetName As String, myTextString As String)
' This macro will delete an entire row based on the presence of a predefined word or set of words.
'If that word or set of words is 'found in a cell, in a specified column, the entire row will be 'deleted
'Note the seperator is a space. To change this modify the split parameter
'EXAMPLE CALL: Call DelRow(1, "AH", "Cut Data", "DEL")
Dim X As Long
Dim Z As Long
Dim LastRow As Long
Dim FoundRowToDelete As Boolean
Dim OriginalCalculationMode As Integer
Dim RowsToDelete As Range
Dim SearchItems() As String
SearchItems = Split(myTextString)
On Error GoTo ResetCalcs
OriginalCalculationMode = Application.Calculation
Application.Calculation = xlCalculationManual
With Worksheets(SheetName)
LastRow = .Cells(.Rows.Count, SearchColumn).End(xlUp).Row
Application.StatusBar = "**** Working on the '" & SheetName & "' Sheet: Number of Rows to be scanned(" & LastRow & "). Deletion keyword " & myTextString & " ***" 'Extra line added
For X = LastRow To DataStartRow Step -1
FoundRowToDelete = False
For Z = 0 To UBound(SearchItems)
If InStr(.Cells(X, SearchColumn).Value, SearchItems(Z)) Then
FoundRowToDelete = True
Exit For
End If
Next
If FoundRowToDelete Then
If RowsToDelete Is Nothing Then
Set RowsToDelete = .Cells(X, SearchColumn)
Else
Set RowsToDelete = Union(RowsToDelete, .Cells(X, SearchColumn))
End If
If RowsToDelete.Areas.Count > 100 Then
RowsToDelete.EntireRow.Delete
Set RowsToDelete = Nothing
End If
End If
Next
End With
If Not RowsToDelete Is Nothing Then
RowsToDelete.EntireRow.Delete
End If
ResetCalcs:
Application.Calculation = OriginalCalculationMode
End Sub
This is my first attempt at VBA, so I apologize for my ignorance. The situation is as follows: I have a spreadsheet that consists of 4 columns and 629 rows. When I am trying to do is iterate through the 4 cells in each row and check for a blank cell. If there is a row that contains a blank cell, I want to cut it from Sheet1 and paste it into the first available row in Sheet2.
(Ideally the number of columns AND the number of rows is dynamic based on each spreadsheet, but I have no idea how to iterate through rows and columns dynamically)
Sub Macro1()
'
' Macro1 Macro
' Move lines containing empty cells to sheet 2
'
' Keyboard Shortcut: Ctrl+r
'
Dim Continue As Boolean
Dim FirstRow As Long
Dim CurrentRow As Long
Dim LastRow As Long
Dim EmptySheetCount As Long
Dim Counter As Integer
'Initialize Variables
LContinue = True
FirstRow = 2
CurrentRow = FirstRow
LastRow = 629
EmptySheetCount = 1
'Sheets(Sheet1).Select
'Iterate through cells in each row until an empty one is found
While (CurrentRow <= LastRow)
For Counter = 1 To 4
If Sheet1.Cells(CurrentRow, Counter).Value = "" Then
Sheet1.Cells(CurrentRow).EntireRow.Cut Sheet2.Cells(EmptySheetCount, "A")
EmptySheetCount = EmptySheetCount + 1
Counter = 1
CurrentRow = CurrentRow + 1
GoTo BREAK
Else
Counter = Counter + 1
End If
Counter = 1
BREAK:
Next
Wend
End Sub
When I run it, I typically get an error around the Sheet1.Cells(CurrentRow, Counter).Value = "" area, so I know I'm referencing sheets incorrectly. I've tried Sheets(Sheet1), Worksheets("Sheet1") and nothing seems to be working. When I do change to Worksheets("Sheet1"), however, it runs and just freezes Excel.
I know I'm doing multiple things wrong, I just know way too little to know what.
Thanks a lot in advance. And sorry for the crap formatting.
There are a few things wrong with your code so rather than go through them individually here is a basic looping version that does what you're after.
Sub moveData()
Dim wksData As Worksheet
Dim wksDestination As Worksheet
Dim lastColumn As Integer
Dim lastRow As Integer
Dim destinationRow As Integer
Set wksData = Worksheets("Sheet1")
Set wksDestination = Worksheets("Sheet2")
destinationRow = 1
lastColumn = wksData.Range("XFD1").End(xlToLeft).Column
lastRow = wksData.Range("A1048576").End(xlUp).Row
For i = lastRow To 1 Step -1 'go 'up' the worksheet to handle 'deletes'
For j = 1 To lastColumn
If wksData.Cells(i, j).Value = "" Then 'check for a blank cell in the current row
'if there is a blank, cut the row
wksData.Activate
wksData.Range(Cells(i, 1), Cells(i, lastColumn)).Cut
wksDestination.Activate
wksDestination.Range(Cells(destinationRow, 1), Cells(destinationRow, lastColumn)).Select
ActiveSheet.Paste
'If required this code will delete the 'cut' row
wksData.Rows(i).Delete shift:=xlUp
'increment the output row
destinationRow = destinationRow + 1
Exit For 'no need to carry on with this loop as a blank was already found
End If
Next j
Next i
set wksData = Nothing
set wksDestination = Nothing
End Sub
There are other ways that will achieve the same outcome but this should give you and idea of how to use loops, sheets, ranges, etc.
The lastColumn and lastRow variables will find the the last column/row of data in the given columns/rows (i.e, in my code it finds the last column of data in row 1, and the last row of data in column A).
Also, you should get into the habit of debugging and stepping through code to identify errors and see exactly what each line is doing (this will also help you learn too).
You might find this of use.
It uses an array variable to store the values of the cells in the row to be moved. It does not use cut and paste, so only transfer the data values, and the code does not require activation of the required sheets.
The destination rows are in the same order as the rows on the original sheet.
The method used to find the last cell used in the row and column is more elegant than other answers given.
Option Explicit
Public Sub test_moveData()
Dim wksData As Worksheet
Dim wksDestination As Worksheet
Set wksData = shtSheet1 ' Use the Codename "shtSheet1" for the worksheet. ie the value of the sheet property that is displayed as "(Name)"
Set wksDestination = shtSheet2
moveData wksData, wksDestination
End Sub
Public Sub moveData(wksData As Worksheet, wksDestination As Worksheet)
Dim ilastColumn As Integer
Dim ilastRow As Integer
Dim iRow As Long
Dim iColumn As Long
Dim iDestinationRowNumber As Integer
Dim MyArray() As Variant
Dim rngRowsToDelete As Range
iDestinationRowNumber = 1
ilastColumn = wksData.Cells(1, wksData.Columns.Count).End(xlToLeft).Column
ilastRow = wksData.Cells(wksData.Rows.Count, 1).End(xlUp).Row
ReDim MyArray(1, ilastColumn)
Set rngRowsToDelete = Nothing
For iRow = 1 To ilastRow Step 1 'No need to go 'up' the worksheet to handle 'deletes'
For iColumn = 1 To ilastColumn
If wksData.Cells(iRow, iColumn).Value = "" Then 'check for a blank cell in the current row
MyArray = wksData.Range(wksData.Cells(iRow, 1), wksData.Cells(iRow, ilastColumn)).Value
wksDestination.Range(wksDestination.Cells(iDestinationRowNumber, 1),
wksDestination.Cells(iDestinationRowNumber, ilastColumn) _
).Value = MyArray
'Store the rows to be deleted
If rngRowsToDelete Is Nothing Then
Set rngRowsToDelete = wksData.Rows(iRow)
Else
Set rngRowsToDelete = Union(rngRowsToDelete, wksData.Rows(iRow))
End If
'increment the output row
iDestinationRowNumber = iDestinationRowNumber + 1
Exit For 'no need to carry on with this loop as a blank was already found
End If
Next iColumn
Next iRow
If Not rngRowsToDelete Is Nothing Then
rngRowsToDelete.EntireRow.Delete shift:=xlUp
End If
Set rngRowsToDelete = Nothing
Set wksData = Nothing
Set wksDestination = Nothing
End Sub
' enjoy