VBA - Loop through rows and delete if multiple values exists - excel

I wish to optimize my code which loops through all rows and deletes it if a certain value exists. However, I am currently looping through > 100000 rows, so I wish to improve the speed.
Main purpose: Loop through all rows and delete it if a) cell(row, "A").value = "X1", b) cell(row, "S").value = "X2", and c) cell(row, "AW").value = "X3".
My current code is as follows:
Call FilterData("A", "X1")
Call FilterData("S", "X2")
Call FilterData("AW", "X3")
Sub FilterData(Column as String, Check as String)
Dim Firstrow As Long
Dim Lastrow As Long
Dim Lrow As Long
Dim CalcMode As Long
Dim ViewMode As Long
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
End With
If Not Sheets("XXX").AutoFilterMode Then
Sheets("XXX").Range("1:1").AutoFilter
End If
Sheets("XXX").Range("A2:BT1048576").Sort _
Key1:=Sheets("XXX").Range(Column & "1"), Order1:=xlAscending
With Sheets("XXX")
.Select
ViewMode = ActiveWindow.View
ActiveWindow.View = xlNormalView
.DisplayPageBreaks = False
Firstrow = .UsedRange.Cells(1).Row
Lastrow = .UsedRange.Rows(.UsedRange.Rows.Count).Row
For Lrow = Lastrow To Firstrow Step -1
With .Cells(Lrow, Column)
If Not IsError(.Value) Then
If .Value = Check Then .EntireRow.Delete
End If
End With
Next Lrow
End With
End Sub

Optimizing by applying a autofiler against the columns you wish to delete, if the values exist, the code deletes them. I made this really quickly, untested, but the logic works, hope this helps! :)
Code Added:
Sub Filter()
Dim ColALookup As String
Dim ColSLookup As String
Dim colAWlookup As String
Dim i As Long
ColALookup = "X1"
ColSLookup = "X2"
colAWlookup = "X3"
With Sheets("XXX")
.Range("A2", .Range("A" & .Rows.Count).End(xlUp)) _
.AutoFilter Field:=1, Criteria1:=Application.Transpose(ColALookup), Operator:=xlFilterValues
.Range("S2", .Range("S" & .Rows.Count).End(xlUp)) _
.AutoFilter Field:=1, Criteria1:=Application.Transpose(ColSLookup), Operator:=xlFilterValues
.Range("AW2", .Range("AW" & .Rows.Count).End(xlUp)) _
.AutoFilter Field:=1, Criteria1:=Application.Transpose(colAWlookup), Operator:=xlFilterValues
.Range("A2", .Range("A" & .Rows.Count).End(xlUp)) _
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
.Range("S2", .Range("S" & .Rows.Count).End(xlUp)) _
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
.Range("AW2", .Range("AW" & .Rows.Count).End(xlUp)) _
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
.AutoFilterMode = False
End With
End Sub

Related

VBA Excel remove duplicate values based on values in other column

I have several codes of the same. I can remove duplicates only for these ones, which have the H-column empty. If the H column contains nonempty cell, the given code must stay.
I tried to work with IsEmpty() function, but it didn't work. The behaviour was as normal.
The code looks like this:
If IsEmpty(shTarget.Range("H" & lRow)) Then
With shTarget.Range("A" & lRow)
.PasteSpecial xlPasteValuesAndNumberFormats
.PasteSpecial xlPasteFormats
.RemoveDuplicates Columns:=1, Header:=xlYes
.Sort Key1:=.Columns(1), Order1:=xlAscending, Header:=xlYes
.WrapText = True
End With
End If
The approach with:
If shTarget.Range("H" & lRow) = "" Then
was exactly the same.
How can I retain the duplicate codes, which have value in other column?
UPDATE:
With this approach:
With shTarget.Range("A" & lRow)
.PasteSpecial xlPasteValuesAndNumberFormats
.PasteSpecial xlPasteFormats
If .Range("H" & lRow).Value = "" Then
.RemoveDuplicates Columns:=1, Header:=xlYes
End If
.Sort Key1:=.Columns(1), Order1:=xlAscending, Header:=xlYes
.WrapText = True
End With
I have an error:
Sort method of Range class failed
UPDATE II
Tried also have this one:
For Each r In rng
If r.Value = "" Then
shTarget.Range("A2:H" & lRow).RemoveDuplicates Columns:=1,
Header:=xlYes
End If
Next r
it still doesn't work. Basically, no difference was observed.
My full code is:
Sub CopyData_Cables(ByRef shSource As Worksheet, shTarget As Worksheet)
Const VHead As String = "A1:H1"
Const VMBom As String = "A2:H100"
shSource.Range(VHead).Copy
With shTarget.Range("A1")
.PasteSpecial xlPasteValues
.PasteSpecial xlPasteFormats
End With
Dim lRow As Long, lRow2 As Long
Dim i As Integer
lRow = shTarget.Cells(Rows.Count, "A").End(xlUp).Row + 1
shSource.Range(VMBom).Copy
Set Rng = shTarget.Range("H2" & lRow)
If IsEmpty(shTarget.Range("H" & lRow)) Then
With shTarget.Range("A" & lRow)
.PasteSpecial xlPasteValuesAndNumberFormats
.PasteSpecial xlPasteFormats
.RemoveDuplicates Columns:=1, Header:=xlYes
.Sort Key1:=.Columns(1), Order1:=xlAscending, Header:=xlYes
.WrapText = True
End With
End If
'If shTarget.Range("H2" & lRow) <> "" Then
'shTarget.Range("A" & lRow).Value = 0
'End If
'For Each r In Rng
' If r.Value = "" Then
'shTarget.Range("A2:A" & lRow).Value = "Kurs!"
' End If
' Next r
shTarget.Columns("A").ColumnWidth = 6.11
shTarget.Columns("B").ColumnWidth = 50
shTarget.Columns("C").ColumnWidth = 50
shTarget.Columns("D").ColumnWidth = 5.44
shTarget.Columns("E").ColumnWidth = 5.89
shTarget.Columns("F").ColumnWidth = 9
shTarget.Columns("G").ColumnWidth = 21.22
shTarget.Columns("H").ColumnWidth = 10.89
shTarget.Rows.EntireRow.AutoFit
For i = 3 To lRow Step 4
shTarget.Range(shTarget.Cells(i, 1), shTarget.Cells(i, 5)).Interior.Color = RGB(235, 235, 235)
shTarget.Range(shTarget.Cells(i, 7), shTarget.Cells(i, 8)).Interior.Color = RGB(235, 235, 235)
Next i
' Reset the clipboard.
Application.CutCopyMode = xlCopy
End Sub
Scan up the sheet deleting the duplicate rows.
Option Explicit
Sub RemoveDuplicates()
Dim ws As Worksheet, dict
Dim lastrow As Long, i As Long, n As Long
Dim key As String
Dim fso, ts
Set fso = CreateObject("Scripting.FilesystemObject")
Set ts = fso.CreateTextFile("debug.txt")
Set dict = CreateObject("Scripting.Dictionary")
Set ws = ThisWorkbook.Sheets("Sheet1")
With ws
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = lastrow To 2 Step -1
If Len(Trim(.Cells(i, "H"))) = 0 Then
key = Trim(.Cells(i, "A"))
If dict.exists(key) Then
'.Cells(i, "A").Interior.Color = vbRed
.Rows(i).Delete
n = n + 1
Else
dict.Add key, i
End If
Else
key = ""
End If
ts.writeline i & " A='" & .Cells(i, "A") & "' H='" _
& .Cells(i, "H") & "' key='" & key & "' n=" & n
Next
End With
ts.Close
MsgBox n & " rows deleted", vbInformation
End Sub

Split data into multiple worksheets using MULTIPLE column filters

There are answers to this question using a single filter. BUT How do you split a worksheet into multiple worksheets based off of more than 1 filter (column). I have this worksheet below.
Name Age Branch Section Dept
Bob 20 1 2 A
Bill 20 1 2 A
Jill 20 1 2 B
Jane 20 1 3 A
Paul 20 2 3 B
Tom 20 2 3 B
I want to split this into multiple worksheets based off of 3 columns (Branch, Section, Dept). The results should look like this:
Name Age Branch Section Dept
Bob 20 1 2 A
Bill 20 1 2 A
Name Age Branch Section Dept
Jill 20 1 2 B
Name Age Branch Section Dept
Jane 20 1 3 A
Name Age Branch Section Dept
Paul 20 2 3 B
Tom 20 2 3 B
How would I write a VBA Excel macro to do this?
Also each worksheet should be named "BRANCH" # & "SECTION" # & "DEPT" letter. (e.g. BRANCH1SECTION2DEPTA)
Currently, I have this VBA code that can do this filtering for 1 column.
Sub SplitandFilterSheet()
'Step 1 - Name your ranges and Copy sheet
'Step 2 - Filter by Department and delete rows not applicable
'Step 3 - Loop until the end of the list
Dim Splitcode As Range
Sheets("Master").Select
Set Splitcode = Range("Splitcode")
For Each cell In Splitcode
Sheets("Master").Copy After:=Worksheets(Sheets.Count)
ActiveSheet.Name = cell.Value
With ActiveWorkbook.Sheets(cell.Value).Range("MasterData")
.AutoFilter Field:=6, Criteria1:="NOT EQUAL TO" & cell.Value, Operator:=xlFilterValues
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
ActiveSheet.AutoFilter.ShowAllData
Next cell
End Sub
I just hacked this together. It seems to do what you described. Notice, I copied the data from C1:E7 and pasted it into AA1, then clicked Data > Remove Duplicates. You can record a Macro to do this and add it to the code, towards the top.
Sub Copy_To_Worksheets()
Dim My_Range As Range
Dim FieldNum As Long
Dim CalcMode As Long
Dim ViewMode As Long
Dim ws2 As Worksheet
Dim Lrow As Long
Dim cell As Range
Dim CCount As Long
Dim WSNew As Worksheet
Dim ErrNum As Long
Set My_Range = Range("A1:E" & LastRow(ActiveSheet))
My_Range.Parent.Select
'Turn off AutoFilter
My_Range.Parent.AutoFilterMode = False
'Change ScreenUpdating, Calculation, EnableEvents, ....
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
ViewMode = ActiveWindow.View
ActiveWindow.View = xlNormalView
ActiveSheet.DisplayPageBreaks = False
'Add a worksheet to copy the a unique list and add the CriteriaRange
Set ws = Worksheets("Data")
With ws
Lrow = .Cells(Rows.Count, "A").End(xlUp).Row
'For Each cell In .Range("A2:A" & Lrow)
For Each c In Range("AA2:AA5")
'Filter the range
My_Range.AutoFilter Field:=3, Criteria1:="=" & c.Value
My_Range.AutoFilter Field:=4, Criteria1:="=" & c.Offset(0, 1).Value
My_Range.AutoFilter Field:=5, Criteria1:="=" & c.Offset(0, 2).Value
Set WSNew = Worksheets.Add(After:=Sheets(Sheets.Count))
On Error Resume Next
WSNew.Name = "Branch" & c.Value & "Section" & c.Offset(0, 1).Value & "Dept" & c.Offset(0, 2).Value
On Error GoTo 0
'Copy the visible data to the new worksheet
My_Range.SpecialCells(xlCellTypeVisible).Copy
With WSNew.Range("A1")
.PasteSpecial Paste:=8
.PasteSpecial xlPasteValues
.PasteSpecial xlPasteFormats
Application.CutCopyMode = False
.Select
End With
Next c
'Next cell
'Delete the ws2 sheet
On Error Resume Next
Application.DisplayAlerts = False
.Delete
Application.DisplayAlerts = True
On Error GoTo 0
End With
'Turn off AutoFilter
'My_Range.Parent.AutoFilterMode = False
If ErrNum > 0 Then
MsgBox "Rename every WorkSheet name that start with ""Error_"" manually" _
& vbNewLine & "There are characters in the name that are not allowed" _
& vbNewLine & "in a sheet name or the worksheet already exist."
End If
'Restore ScreenUpdating, Calculation, EnableEvents, ....
'My_Range.Parent.Select
ActiveWindow.View = ViewMode
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub
Function LastRow(sh As Worksheet)
On Error Resume Next
LastRow = sh.Cells.Find(What:="*", _
After:=sh.Range("A1"), _
Lookat:=xlPart, _
LookIn:=xlValues, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
On Error GoTo 0
End Function
Before:
After:
I am adding some modified code below, to address your last question. Use the code below, and keep the Function named 'LastRow'.
Sub TryThis()
Dim My_Range As Range
Dim FieldNum As Long
Dim CalcMode As Long
Dim ViewMode As Long
Dim ws2 As Worksheet
Dim Lrow As Long
Dim cell As Range
Dim CCount As Long
Dim WSNew As Worksheet
Dim ErrNum As Long
Set My_Range = Range("A1:E" & LastRow(ActiveSheet))
My_Range.Parent.Select
'Turn off AutoFilter
My_Range.Parent.AutoFilterMode = False
'Change ScreenUpdating, Calculation, EnableEvents, ....
With Application
CalcMode = .Calculation
.Calculation = xlCalculationManual
.ScreenUpdating = False
.EnableEvents = False
End With
ViewMode = ActiveWindow.View
ActiveWindow.View = xlNormalView
ActiveSheet.DisplayPageBreaks = False
'Add a worksheet to copy the a unique list and add the CriteriaRange
Set ws = Worksheets("Data")
With ws
Lrow = .Cells(Rows.Count, "A").End(xlUp).Row
For Each c In Range("AA2:AA5")
'Filter the range
My_Range.AutoFilter Field:=3, Criteria1:="=" & c.Value
My_Range.AutoFilter Field:=4, Criteria1:="=" & c.Offset(0, 1).Value
My_Range.AutoFilter Field:=5, Criteria1:="=" & c.Offset(0, 2).Value
Set WSNew = Worksheets.Add(After:=Sheets(Sheets.Count))
On Error Resume Next
WSNew.Name = "Branch" & c.Value & "Section" & c.Offset(0, 1).Value & "Dept" & c.Offset(0, 2).Value
On Error GoTo 0
'Copy the visible data to the new worksheet
My_Range.SpecialCells(xlCellTypeVisible).Copy
With WSNew.Range("A1")
.PasteSpecial Paste:=8
.PasteSpecial xlPasteValues
.PasteSpecial xlPasteFormats
Application.CutCopyMode = False
.Select
End With
Columns("C:E").Select
Selection.ClearContents
Next c
End With
'Turn off AutoFilter
'My_Range.Parent.AutoFilterMode = False
If ErrNum > 0 Then
MsgBox "Rename every WorkSheet name that start with ""Error_"" manually" _
& vbNewLine & "There are characters in the name that are not allowed" _
& vbNewLine & "in a sheet name or the worksheet already exist."
End If
'Restore ScreenUpdating, Calculation, EnableEvents, ....
'My_Range.Parent.Select
ActiveWindow.View = ViewMode
With Application
.ScreenUpdating = True
.EnableEvents = True
.Calculation = CalcMode
End With
End Sub

Autofilter: Copy & paste. How to Offset when inserting copied data?

I need to offset the data I'm copying, so the new data doesn't erase the old data.
I tried to different methods with offset. But I'm clearly doing something wrong
Dim lastrow As Long: lastrow = Cells(Rows.Count, "A").End(xlUp).Row
Dim lastrow2 As Long: lastrow = Cells(Rows.Count, "h").End(xlUp).Row
Application.ScreenUpdating = False
With Sheets("Test")
.AutoFilterMode = False
.Range("A1:C" & lastrow).AutoFilter field:=3, Criteria1:="=TEST Sheet", Operator:=xlFilterValues
.Range("A1:C" & lastrow).AutoFilter field:=2, Criteria1:="=New"
.Range("A2:A" & lastrow).SpecialCells(xlCellTypeVisible).Copy .Range("H26").Offset(lastrow2, 0)
.AutoFilterMode = False
End With
Application.ScreenUpdating = True
So basically all the copied data goes into to Row H, but the list should keep getting larger.

Delete data older than a month for every sheet

I have an Excel file with many sheets, and I would like to filter old data at the end of the month and delete it. I can't always clear the data on the first, and sometimes users will work on the workbook before I can set it up for the current month.
This is the code I currently have:
Private Sub CommandButton4_Click()
Dim ws As Worksheet
Dim Rng As Range
Dim LastRow As Long, LastCol As Long
response = MsgBox("Tem a Certeza que quer Limpar todo os Dados?", vbYesNo)
If response = vbNo Then Exit Sub
For Each ws In ThisWorkbook.Worksheets
If ws.Name <> "Formularios" Then
If ws.Name <> "Coordenador" Then
If ws.Name <> "LookupList" Then
With ws
.Unprotect Password:=pass
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
LastCol = .Range("A" & .Columns.Count).End(xlToLeft).Column
Set Rng = .Range(.Cells(1, 1), .Cells(LastRow, LastCol))
End With
With Rng
.AutoFilter Field:=1, Criteria1:=xlFilterLastMonth
.SpecialCells(xlCellTypeVisible).Offset(1, 0).Resize(.Rows.Count).Rows.Delete
End With
With ws
.AutoFilterMode = False
If .FilterMode = True Then
.ShowAllData
End If
.Protect Password:=pass
End With
End If
End If
End If
Next
End Sub
I get an Error 1004 object on this line:
.SpecialCells(xlCellTypeVisible).Offset(1, 0).Resize(.Rows.Count).Rows.Delete
This is the form I typically use.
With ws
.Unprotect Password:=pass
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
LastCol = .cells(1, .Columns.Count).End(xlToLeft).Column
Set Rng = .Range(.Cells(1, 1), .Cells(LastRow, LastCol))
End With
With Rng
.AutoFilter Field:=1, Criteria1:=xlFilterLastMonth, Operator:=xlFilterDynamic
with .resize(.rows.count-1, .columns.count).offset(1, 0)
if cbool(application.subtotal(103, .cells)) then
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
end if
end with
End With
I changed the initial range (rng) to be only the second row and took out the offset and resize. Since the range is only used in this subroutine, why is it being resized?
I tested it with 5 or 6 dates in this month and last, even putting them out of order and it worked as expected.
sub stest()
With Worksheets(1)
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
Set Rng = .Range(.Cells(2, 1), .Cells(LastRow, 1))
End With
With Rng
.AutoFilter Field:=1, Criteria1:=xlFilterLastMonth, Operator:=xlFilterDynamic
.offset(1,0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
End Sub

Filter looping issue

I'm having an issue with this code that is suppsed to get data from another workbook and specific worksheet. It's supposed to sort and filter out data that is in Column C when I enter a specific data that I enter on my workbook. The code is working up until the sort and filter part and it seems like it's duplicating the data on my workbook instead of just filtering and pulling the data needed. Here is the code I will separate it in parts for better understanding.
Beginning of code that opens the other workbook:
The 3rd to last part of the code is what is highlighted in the debugging process, the "workbooks(mname) part towards the end.
Workbooks(mname).Sheets(msheet).Range(Cells(1, 1), Cells(1, LastCol)).Copy Destination:=Workbooks("Master_RRR.xlsm").Sheets("MGPR1").Range("A1")
I = 17
Do While Workbooks("Master_RRR.xlsm").Sheets("Master log").Cells(17, "Y").Value <> ""
mbank = Workbooks("Master_RRR.xlsm").Sheets("Master log").Cells(17, "Y").Value
match = Application.WorksheetFunction.match(mbank, Workbooks(mname).Sheets(msheet).Range("C1:C" & LastRow), 0)
repeat = Application.WorksheetFunction.CountIf(Workbooks(mname).Sheets(msheet).Range("C1:C" & LastRow), mbank)
till = Application.WorksheetFunction.CountA(Workbooks("Master_RRR.xlsm").Sheets("MGPR1").Range("C:C"))
Workbooks(mname).Sheets(msheet).Range(Cells(match, "C"), Cells(match + repeat - 1, LastCol)).Copy Destination:=Workbooks("Master_RRR.xlsm").Sheets("MGPR1").Range("A" & till + 1)
I = I + 1
Loop
Here is the beginning of the code:
Sub getdata()
Dim mastername As String
Dim count As Long
Dim match As Long
Dim repeat As Long
Dim path As String
Dim status As String
Dim name As String
Dim mpath As String
Dim cpath As String
Dim LastRow As Long
Dim LastCol As Integer
Dim mbank As String
Dim mname As String
mpath = Sheets("Master log").Cells(14, "Y").Value
mname = Sheets("Master log").Cells(15, "Y").Value
msheet = Sheets("Master log").Cells(16, "Y").Value
Sheets("MGPR1").Range("A1:AA50000").ClearContents
name = Application.ActiveWorkbook.name
cpath = Application.ActiveWorkbook.path & "\"
Windows(name).Activate
'--open Management report workbook if not already open
If CheckFileIsOpen(mname) = False Then
Workbooks.Open mpath & mname
End If
'-------------------------------------------
Windows(mname).Activate
For Each ws In ActiveWorkbook.Worksheets
ws.Visible = xlSheetVisible
If ws.Visible = True Then
End If
Next ws
Sheets(msheet).Select
'select full data
With ActiveSheet
LastRow = .Cells(.Rows.count, "A").End(xlUp).Row
End With
With ActiveSheet
' LastCol = .Cells(1, .Columns.count).End(xlToLeft).Column
LastCol = 22
End With
'--------------------- put filter
Range(Cells(1, 1), Cells(LastRow, LastCol)).Select
Selection.AutoFilter
Range("K2").Select
ActiveWorkbook.Worksheets(msheet).AutoFilter.Sort.SortFields. _
Add Key:=Range("C1:C" & LastRow), SortOn:=xlSortOnValues, Order:=xlAscending, _
DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets(msheet).AutoFilter.Sort
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Range("A1").Select
Range(Cells(1, 1), Cells(LastRow, LastCol)).Select
Selection.AutoFilter
Range("H5").Select

Resources