VBA slow process for removing rows based on condition - vba

I have a VBA Excel code with that checks values in a specific column. If the row in that column contains the value 'Delete' and then deletes the row.
The code works well, but it is really slow. Any ideas on how to get the code run faster?
Dim rng1 As Range
Dim i As Integer, counter As Integer
'Set the range to evaluate to rng.
Set rng1 = Range("g1:g1000")
'initialize i to 1
i = 1
'Loop for a count of 1 to the number of rows
'in the range that you want to evaluate.
For counter = 1 To rng1.Rows.Count
'If cell i in the range1 contains an "Delete"
'delete the row.
'Else increment i
If rng1.Cells(i) = "Delete" Then
rng1.Cells(i).EntireRow.Delete
Else
i = i + 1
End If
Next
Thanks
c.

Sub deletingroutine()
Dim r As Range
For Each r In Range("g1:g1000")
If r = "delete" Then r.EntireRow.Delete
Next r
End Sub

I managed to find a solution with the Autofilter function.
Hope it helps someone
Selection.AutoFilter
Set ws = ActiveWorkbook.Sheets("UploadSummary")
lastRow = ws.Range("G" & ws.Rows.Count).End(xlUp).Row
Set rng = ws.Range("G1:G" & lastRow)
' filter and delete all but header row
With rng
.AutoFilter Field:=7, Criteria1:="delete" ' 7 refers to the 7th column
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With

Try sorting the rows (on collumn G) then deleting all marked ("delete") rows in one action. That is much faster.

Related

Excel Delete empty row in a looped range [duplicate]

This macro is designed to compare the data in column C and D and if C does not match D in a certain row, it deletes the entire tow. The problem is that it deletes the headers in Row 1 on the Excel sheet because they don't match. How do I run the macro for rows 2 through 9999 instead of all 9999 rows.
Sub deleteNonMatchingRows()
Dim i As Long
For i = 9999 To 1 Step -1 ' it will scan 9999 rows of the sheet. This number can be increased for larger sheets
If Range("C" & i) <> Range("D" & i) Then
Range("C" & i).EntireRow.Delete
End If
Next
End Sub
If you use a descriptive variable naming, eg. rename i into iRow you will never forget that this is your row counter, that is counting from row 9999 to row 1 in For iRow = 9999 To 1 Step -1. So you need to change the 1 into a 2 to omit the first row.
I recommend to use a dynamic start for your loop that automatically finds the last used row. This prevents unnecessary loop steps and you don't need to increase it for larger worksheets.
Option Explicit
Public Sub DeleteNonMatchingRows()
Dim LastRow As Long
LastRow = Cells(Rows.Count, "C").End(xlUp).Row 'find last used row in column C
Dim iRow As Long
For iRow = LastRow To 2 Step -1
If Range("C" & iRow) <> Range("D" & iRow) Then
'Range("C" & iRow).EntireRow.Delete
Rows(iRow).Delete 'directy delete a row
End If
Next iRow
End Sub
Deletion of a row is an operation that takes quite some time. Thus, it is a good idea to make all deletions at once, uniting all rows to be deleted in a specific range wholeRange:
Option Explicit
Public Sub DeleteNonMatchingRows()
Dim LastRow As Long
LastRow = Cells(Rows.Count, "C").End(xlUp).Row
Dim wholeRange As Range
Dim iRow As Long
For iRow = LastRow To 2 Step -1
If Range("C" & iRow) <> Range("D" & iRow) Then
If wholeRange Is Nothing Then
Set wholeRange = Rows(iRow)
Else
Set wholeRange = Union(wholeRange, Rows(iRow))
End If
End If
Next iRow
If Not wholeRange Is Nothing Then
wholeRange.Select 'delete this row
Stop 'delete this row
wholeRange.Delete
End If
End Sub
Once you run the code, it will stop on the Stop line. You will be able to see the range, which is to be deleted. The range will be selected. Once you see it, it is a good idea to delete the two rows, mentioned in the comments, you are not going to need them any more.
you can avoid loops:
Sub deleteNonMatchingRows()
With Range("C2", Cells(Rows.Count, "C").End(xlUp)) ' reference column C cells from row 2 doen to last not empty one
With .Offset(, .Parent.UsedRange.Columns.Count) ' reference referenced range offset by active sheet used range columnns (to be sure you'r not overwriting already filled cells)
.FormulaR1C1 = "=IF(RC3<>RC4,1,"""")" ' have referenced cells show a "1" if corresponding cells in column C and D match
.SpecialCells(xlCellTypeFormulas, xlNumbers).EntireRow.Delete ' delete all rows whose referenced column formula result is a number
.ClearContents ' clear referenced range
End With
End With
End Sub

Find non zero value in column G on sheet 1, return value of Column C in that row to sheet 2 (VBA)

My first sheet is set up like this:
I want to find the non zero values in column G. Then I want to read the corresponding name in column C. I, then, want to return the value of the name to a cell on Sheet 2.
At this point, it doesn't matter what cell it returns to in sheet 2. It sounds like a VLOOKUP or INDEXMATCH but my VBA isn't good enough to figure out the formatting of it. This is some code that I tried and I can get it to return the name. But I don't know how to do it for all non zeros or how to have it print to sheet 2. Need a loop or need to figure out look ups!
code:
For Each c In Range("G6").Cells
If c.Value > 0 Then
PlayerName = Range(Cells(Selection.Row, 3).Address).Value
End If
Exit For
Next c
The following code will find the first row which has a number greater than 0 in column G (starting at row 6), and write the value in column C of that row to cell X5 of Sheet2.
With Worksheets("Sheet1")
For Each c In .Range("G6", .Cells(.Rows.Count, "G").End(xlUp)).Cells
If c.Value > 0 Then
Worksheets("Sheet2").Cells(5, "X").Value = c.Offset(0, -4).Value
Exit For ' Moved this inside the `If`, otherwise it will exit as soon as
' the first cell in the range is processed, irrespective of whether
' it was greater than 0 or not
End If
Next c
End With
Iterative version:
Dim s2Row as Long
s2Row = 5
With Worksheets("Sheet1")
For Each c In .Range("G6", .Cells(.Rows.Count, "G").End(xlUp)).Cells
If c.Value > 0 Then
Worksheets("Sheet2").Cells(s2Row, "X").Value = c.Offset(0, -4).Value
s2Row = s2Row + 1
End If
Next c
End With
Here is the logic you'll need. Will you be able to build the macro with this logic? It will help you understand how to maneuver rows that are greater than zero. Then you copy the column on that row y9ou need and paste it to the other sheet.
Sub macro1()
Dim myRng As Range, lastRow As Long
lastRow = ActiveSheet.Range("G65536").End(xlUp).Row
Set myRng = Sheet1.Range("G1:G" & lastRow)
For Each Rng In myRng
If IsNumeric(Rng.Value) And Rng.Value > 0 Then
Debug.Print "Cell " & Rng.Address & " has the number " & Rng.Value & " in row " & Rng.Row
End If
Next Rng
End Sub
Yes, except "G" is a column, not a row. Replace the debug.print line with WorkSheets("sheet name to copy from here").Rows(rng.row).Copy Destination:=WorkSheets("sheet name to copy to here").Range("A" & rowCounterVariable). Of course, change the sheet names to your actual sheet names.
Here I set the first row at 2 on the page to copy to. If you need to set it to the first available row then you need to research how to find the last used row on that page. Put these exact terms into Google "VBA EXCEL HOW TO FIND LAST USED ROW". I have an example of finding the last used row for the activesheet inside the code. We could give you fish today, and teach you how to fish. But you need to catch your own. We're not here to write code for you.
Sub macro2()
Dim myRng As Range, lastRow As Long, rowCounterVariable as long
rowCounterVariable = 2
lastRow = ActiveSheet.Range("G65536").End(xlUp).Row
Set myRng = Sheet1.Range("G1:G" & lastRow)
For Each Rng In myRng
If IsNumeric(Rng.Value) And Rng.Value > 0 Then
WorkSheets("sheet name to copy from here").Rows(rng.row).Copy Destination:=WorkSheets("sheet name to copy to here").Range("A" & rowCounterVariable)
rowCounterVariable = rowCounterVariable + 1
End If
Next Rng
End Sub

Delete empty rows of a table

Hello I want to delete empty rows of a table and i am finding issues.
Dim rng As Range
rng = Sheets("NewForecast").ListObjects("Table").Range.Select
If rng.Rows = 0 Then
rng.EntireRow.Delete
End If
I don't know how to write it, I tried several ways, looked for here, but could not find an specific solution. I want delete if the Row is completely empty. Any help much appreciated!
I managed to work on that! Thanks for all who actually opened my mind. Look below, it is simple and doing what I want, macro goes faster too.
Range("Table[#Headers]").Select
Selection.AutoFilter
ActiveSheet.ListObjects("Table").Range.AutoFilter Field:=2, Criteria1:="="
Range("Table").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.EntireRow.Delete
Range("Table[#Headers]").Select
ActiveSheet.ShowAllData
Try something like
Dim ws as Worksheet
Set ws = ActiveWorkbook.Worksheets("SHEET NAME HERE")
Dim lRow as long
Dim rng as range
lRow = ws.Range("A" & Rows.Count).end(xlUp).row
'Assuming your table starts in column A, put in start/end row numbers
For each rng in ws.Range("A1:A" & lRow)
If ws.Range("A" & rng.row) = vbNullString then
ws.Rows(rng.row).Delete
End if
Next rng
When trying to delete rows in a sheet, always use a backward For loop (For i = 100 to 1 Step -1 for instance).
When checking if a certain Range or Row is completely empty, the WorksheetFunction.CountA comes in quite handy.
Option Explicit
Sub DeleteEmptyRows()
Dim Rng As Range
Dim LastRow As Long
Dim lRow As Long
With Sheets("NewForecast")
Set Rng = .ListObjects("Table").Range
' find last row in "Table"
LastRow = .ListObjects("Table").Range.Rows.Count
' loop through all "Table" rows, loop backwards when deleting
For lRow = LastRow To 2 Step -1
' use CountA to check if entire row is empty
If WorksheetFunction.CountA(.Rows(lRow)) = 0 Then
.Rows(lRow).EntireRow.Delete
End If
Next
End With
End Sub
This is possible without looping.
Filter the table so the values showing are the ones you want to delete
Find the first "deletable" row (where cell.value = "") in the last column of the sheet (will most typically be blank, most people don't use the last column),
Then find the last "deletable" row (where cell.value = "").
Then use this:
Rows(firstRow & ":" & lastRow).EntireRow.Delete
This can be expensive (take a long time) if your field of values is very large but works on sheets as well as tables and is faster (better) than looping.

Conditional Delete in VBA

I am trying to piece together code to make my macro work correctly. This approach has served me well in the past but I cannot seem to adapt any code correctly.
I found the following
Sub way()
Dim Cell As Range
For Each Cell In Range("A1").CurrentRegion
If Len(Cell) < 2 Then Cell.EntireRow.Delete
Next Cell
End Sub
I can adapt the If Len(Cell) criteria to my liking. For example = 10
I do not know how to adapt the code to make it search through all cells in column A and delete the appropriate rows. The code above only does it for A1.
Ideally I would like to delete all rows with cells in column A that have a length of 10 characters. Or alternatively, with a completely different set of code, delete all other rows that do not contain cells in column A that have a length of 10 characters.
When deleting rows it is best to loop backwards:
Sub way()
Dim ws As Worksheet
Dim i As Long
Dim lastrow As Long
Set ws = ActiveSheet
lastrow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
For i = lastrow To 1 Step -1
If Len(ws.Cells(i, 1)) < 2 Then ws.Rows(i).Delete
Next i
End Sub
Essentially, your loop is going through every cell in the Range.CurrentRegion property radiating out from A1. Your narrative expresses that you only want to examine column A but delete the Range.EntireRow property.
The For .. Next loop stepping backwards proposed by Scott Craner is likely your best bet but if you are more comfortable with a For ... Each In loop then yours can be adjusted.
Sub way()
Dim cl As Range, dels As Range
With Worksheets("Sheet1")
For Each cl In .Range("A1").CurrentRegion.Columns(1).Cells
If Len(cl.Value2) = 10 Then
If Not dels Is Nothing Then
Set dels = Union(dels, cl)
Else
Set dels = cl
End If
End If
Next cl
End With
If Not dels Is Nothing Then _
dels.EntireRow.Delete
End Sub
The logic to delete rows that did not have a value in column A that was 10 characters, symbols or digits long would be If Len(cl.Value2) <> 10 Then.
I haven't checked for syntax, but something like this should work:
dim idx as integer
idx = 1
while Range("A" + cstr(idx)).value <> ""
'insert your delete logic here...
idx = idx + 1
wend

Infinite loop while gathering datasets from several worksheets

This is my first time to code in VBA.
I have several worksheets in a file and they are in order by dates.
So what I am trying to do is to collect data sets in a worksheet if they have the same period of time.
date1 value1
date2 value2
date3 value3
Since they are in order I just compare the first date values and if they are different it moves on to the next worksheet. If they are the same then copy the value and do the same process until it reaches the last worksheet.
However it copies one worksheet fine but after that Excel freezes.
I would be appreciated if you find any errors or give me other suggestions to do it.
Following is my code:
Sub matchingStock()
Dim sh1 As Worksheet, sh2 As Worksheet
' create short references to sheets
' inside the Sheets() use either the tab number or name
Set sh1 = Sheets("combined")
Dim col As Long
'since first column is for Tbill it stock price should place from the third column
col = 3
Dim k As Long
'go through all the stock worksheets
For k = Sheets("WLT").Index To Sheets("ARNA").Index
Set sh2 = Sheets(k)
' Create iterators
Dim i As Long, j As Long
' Create last rows values for the columns you will be comparing
Dim lr1 As Long, lr2 As Long
' create a reference variable to the next available row
Dim nxtRow As Long
' Create ranges to easily reference data
Dim rng1 As Range, rng2 As Range
' Assign values to variables
lr1 = sh1.Range("A" & Rows.Count).End(xlUp).Row
lr2 = sh2.Range("A" & Rows.Count).End(xlUp).Row
If sh1.Range("A3").Value = sh2.Range("A3").Value Then
Application.ScreenUpdating = False
' Loop through column A on sheet1
For i = 2 To lr1
Set rng1 = sh1.Range("A" & i)
' Loop through column A on sheet1
For j = 2 To lr2
Set rng2 = sh2.Range("A" & j)
' compare the words in column a on sheet1 with the words in column on sheet2
'Dim date1 As Date
'Dim date2 As Date
'date1 = TimeValue(sh1.Range("A3"))
'date2 = TimeValue(sh2.Range("A3"))
sh1.Cells(1, col).Value = sh2.Range("A1").Value
' find next empty row
nxtRow = sh1.Cells(Rows.Count, col).End(xlUp).Row + 1
' copy the word in column A on sheet2 to the next available row in sheet1
' copy the value ( offset(0,1) Column B ) to the next available row in sheet1
sh1.Cells(nxtRow, col).Value = rng2.Offset(0, 6).Value
'when the date is different skip to the next worksheet
Set rng2 = Nothing
Next j
Set rng1 = Nothing
Next i
'sh3.Rows("1:1").Delete
Else
GoTo Skip
End If
Skip:
col = col + 1
Next k
End Sub
I cannot identify a specific error so this is a list of suggestions that may help you identify the error and may help improve your code.
Suggestion 1
Do you think the Else block of If-Then-Else-End-If is compulsory?
If sh1.Range("A3").Value = sh2.Range("A3").Value Then
:
Else
GoTo Skip
End If
Skip:
is the same as:
If sh1.Range("A3").Value = sh2.Range("A3").Value Then
:
End If
Suggestion 2
I do not like:
For k = Sheets("WLT").Index To Sheets("ARNA").Index
The value of property Index for a worksheet may not what you think it is. This may not give you the set or sequence of worksheets you expect. Do you want every worksheet except "Combined"? The following should be more reliable:
For k = 1 To Worksheets.Count
If Worksheets(k).Name <> sh1.Name Then
:
End If
Next
Suggestion 3
You use:
.Range("A" & Rows.Count)
.Range("A3")
.Cells(1, col).Value
.Cells(Rows.Count, col)
rng2.Offset(0, 6)
All these methods of identifying a cell or a range have their purposes. However, I find it confusing to use more than one at a time. I find .Cells(row, column) and .Range(.Cells(row1, column1), .Cells(row2, column2)) to be the most versatile and use them unless there is a powerful reason to use one of the other methods.
Suggestion 4
I cannot decypher what this code is attempting to achieve.
You say: "I have several worksheets in a file and they are in order by dates. So what I am trying to do is to collect data sets in a worksheet if they have the same period of time."
If you have set Worksheet("combined").Range("A3").Value to a particular date and you want to collect data from all those sheets with the same value in cell A3 then the outer For-Loop and the If give this effect. But if so, if does not matter how the worksheets are ordered. Also you start checking cell values from row 2 which suggests row 3 is a regular data row.
The outer loop is for each worksheet, the next loop is for each row in "combined" and the inner loop is for each row in the worksheet selected by the outer loop. The middle loop does not appear to do anything but set rng1 which is not used.
Perhaps you can add an explanation of what you are trying to achieve.
Suggestion 5
Are you trying to add an entire column of values from the source worksheets to "Combined". The macro below:
Identifies the next free row in column A of "Combined"
Identifies the last used row in column A of "Sheet2"
Assumes the first interesting row of "Sheet2" is 2.
Adds the entire used range of column A of "Sheet2" (complete with formatting) to the end of "Combined"'s column A in a single statement.
This may demonstrate a better way of achieving the effect you seek.
Sub Test()
Dim RngSrc As Range
Dim RngDest As Range
Dim RowCombNext As Long
Dim RowSrcFirst As Long
Dim RowSrcLast As Long
With Worksheets("Combined")
RowCombNext = .Cells(Rows.Count, "A").End(xlUp).Row + 1
Set RngDest = .Cells(RowCombNext, "A")
End With
With Worksheets("Sheet2")
RowSrcFirst = 2
RowSrcLast = .Cells(Rows.Count, "A").End(xlUp).Row
Set RngSrc = .Range(.Cells(RowSrcFirst, "A"), .Cells(RowSrcLast, "A"))
End With
RngSrc.Copy Destination:=RngDest
End Sub