I am currently building a macro to format a sheet of data as well as to remove inapplicable rows of data. Specifically, I am looking to delete rows where Column L = "ABC" as well as delete rows where Column AA <> "DEF".
So far I have been able to achieve the first objective, but not the second. The existing code is:
Dim LastRow As Integer
Dim x, y, z As Integer
Dim StartRow, StopRow As Integer
For x = 0 To LastRow
If (Range("L1").Offset(x, 0) = "ABC") Then
Range("L1").Offset(x, 0).EntireRow.Delete
x = x - 1
End If
It is normally much quicker to use AutoFilter rather than loop Ranges
The code below creates a working column, then use a formula to detect delete criteria and then autofilter and delete the result records
The working column puts a formula
=OR(L1="ABC",AA1<>"DEF")
into row 1 of the first blank column then copies down as far ar the true used range. Then any TRUE records are quicklly deleted with AutoFilter
Sub QuickKill()
Dim rng1 As Range, rng2 As Range, rng3 As Range
Set rng1 = Cells.Find("*", , xlValues, , xlByColumns, xlPrevious)
Set rng2 = Cells.Find("*", , xlValues, , xlByRows, xlPrevious)
Set rng3 = Range(Cells(rng2.Row, rng1.Column), Cells(1, rng1.Column))
Application.ScreenUpdating = False
Rows(1).Insert
With rng3.Offset(-1, 1).Resize(rng3.Rows.Count + 1, 1)
.FormulaR1C1 = "=OR(RC12=""ABC"",RC27<>""DEF"")"
.AutoFilter Field:=1, Criteria1:="TRUE"
.EntireRow.Delete
On Error Resume Next
'in case all rows have been deleted
.EntireColumn.Delete
On Error GoTo 0
End With
Application.ScreenUpdating = True
End Sub
Using a loop:
Sub test()
Dim x As Long, lastrow As Long
lastrow = Cells(Rows.Count, 1).End(xlUp).Row
For x = lastrow To 1 Step -1
If Cells(x, 12).Value = "ABC" or Cells(x, 27) <> "DEF" Then
Rows(x).Delete
End If
Next x
End Sub
Using autofilter (probably faster):
Sub test2()
Range("a1").AutoFilter Field:=12, Criteria1:="ABC", Operator:=xlOr, _
Field:=28, Criteria1:="<>""DEF"""
'exclude 1st row (titles)
With Intersect(Range("a1").CurrentRegion, _
Range("2:60000")).SpecialCells(xlCellTypeVisible)
.Rows.Delete
End With
ActiveSheet.ShowAllData
End Sub
Cell with number 12 is "L" and number 27 is "AA"
Dim x As Integer
x = 1
Do While x <= ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row
If (Cells(x, 12) = "ABC") Then
ActiveSheet.Rows(x).Delete
Else
If (Cells(x, 27) <> "DEF") And (Cells(x, 27) <> "") Then
ActiveSheet.Rows(x).Delete
Else
x = x + 1
End If
End If
Loop
End Sub
Sub test()
Dim bUnion As Boolean
Dim i As Long, lastrow As Long
Dim r1 As Range
Dim v1 As Variant
lastrow = Cells(Rows.Count, 1).End(xlUp).Row
v1 = ActiveSheet.Range(Cells(1, 12), Cells(lastrow, 27)).Value2
bUnion = False
For i = 1 To lastrow
If v1(i, 1) = "ABC" Or v1(i, 16) <> "DEF" Then
If bUnion Then
Set r1 = Union(r1, Cells(i, 1))
Else
Set r1 = Cells(i, 1)
bUnion = True
End If
End If
Next i
r1.EntireRow.Delete
End Sub
Related
Sub DeleteExtraValues ()
Dim I as Integer, strValueToFind As String, lngRows As Long, she As Worksheet
Set an = ThisWorkbook.ActiveSheet
LngRows = sh.Range(“A1048576”).End(xlUp).Row
strValueToFind = “DCAP”
For I = 1 To lngRows
If InStr(Cells(I,1).Value, strValueToFind) = 0 Then
If Cells(I,1).Value = “” Then
Else
Rows(I).Delete
I = I-1
End If
End If
Next I
End Sub
When running this, it will delete the cells above the data I want to keep and then will stop once it gets to the first cell that contains “DCAP”. I need it also to delete any unnecessary information after the last cell that contains “DCAP”.
Try this code. It removes every row that doesn't contain DCAP in the first column.
Dim r As Long
Dim LastRow As Long
r = 1
LastRow = Cells.Find("*", [A1], , , xlByRows, xlPrevious).Row
Do Until r > LastRow
DoEvents
If InStr(1, Cells(r, 1), "DCAP") > 0 Then
r = r + 1
Else
Cells(r, 1).EntireRow.Delete
LastRow = LastRow - 1
End If
Loop
MsgBox
"done"
Try this...
Dim rng As Range
Set rng = ActiveSheet.Range("A1").CurrentRegion 'depending on your data you may have to change to a specific range using last row and column
' change the `Field` to the column that contains "DCAP"
With rng
.AutoFilter Field:=9, Criteria1:="<>DCAP", Operator:=xlAnd 'select all cells that are not "DCAP"
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete 'don't delete the header row
.AutoFilterMode = False
End With
Detailed Problem
I'm attempting to write a VBA code that would loop through Column D,
if it finds Cells(i,"D") = "Good" then the code would search the entire column D for that value in cells (i,"D") and change all it's value to "Good"
Here is an Image on before the code.
Here is an Image after the code.
My Attempt:
Dim i As Integer
For i = 1 To Rows.Count
If Cells(i, "m") = "Good" Then
x = Cells(i, "m")
Next i
I think you would have to store the value ( ID Number ) and then search for it which I assigned "X". Once "X" is found change the status to "Good"
Use an AutoFilter
Option Explicit
Sub makeGood()
Dim i As Long, tmp As Variant
Dim dict As Object, k As Variant
'late bind a dictionary
Set dict = CreateObject("scripting.dictionary")
dict.CompareMode = vbTextCompare
With Worksheets("sheet11")
'remove any existing autofilters
If .AutoFilterMode Then .AutoFilterMode = False
'collect values from column D
tmp = .Range(.Cells(2, "D"), .Cells(.Rows.Count, "D").End(xlUp)).Value
'build dictionary of unique ID NUMs
For i = LBound(tmp, 1) To UBound(tmp, 1)
dict.Item(tmp(i, 1)) = vbNullString
Next i
'work with D:G range
With .Range(.Cells(1, "D"), .Cells(.Rows.Count, "G").End(xlUp))
'loop through unique ID NUMs
For Each k In dict.Keys
'autofilter on key
.AutoFilter field:=1, Criteria1:=k, visibledropdown:=False
'autofilter on Good
.AutoFilter field:=4, Criteria1:="good", visibledropdown:=False
'check for visible cells
If Application.Subtotal(103, .Offset(1, 0).Cells) > 0 Then
'remove the Good autofilter
.AutoFilter field:=4
'step down off the header and put Good in the filtered cells
With .Resize(.Rows.Count - 1, 1).Offset(1, 3)
.SpecialCells(xlCellTypeVisible) = "Good"
End With
End If
'clear autofilter
.AutoFilter field:=1
.AutoFilter field:=4
Next k
End With
End With
End Sub
May be a bit convoluted, but here is an idea.
Sub f(strSearchFor as string)
Dim r As Excel.Range
Dim d As New Scripting.Dictionary
Set r = Range("a1:b10")
For Each c In r.Columns(2).Cells
If StrComp(c.Value, strSearchFor, vbTextCompare) = 0 Then
If Not d.Exists(c.Value) Then
d.Add c.Offset(0, -1).Value, c.Value
End If
End If
Next c
For Each c In r.Columns(1).Cells
If d.Exists(c.Value) Then
c.Offset(0, 1).Value = d(c.Value)
End If
Next c
Set r = Nothing
Set d = Nothing
End Sub
You can add a helper column and do it with a formula only:
Add the following formula eg. in H2 (of your example) and pull it down:
=IF(COUNTIFS(D:D,D2,G:G,"Good")>0,"Good",G2)
You could try:
Option Explicit
Sub trst()
Dim i As Long, y As Long, LastRow As Long
Dim ID As String, Status As String
With ThisWorkbook.Worksheets("Sheet1") '<- Change Workbook / Sheet names
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
For i = 2 To LastRow
ID = .Range("D" & i).Value
Status = .Range("G" & i).Value
For y = 2 To LastRow
If ID = .Range("D" & y).Value Then
.Range("G" & y).Value = Status
End If
Next y
Next i
End With
End Sub
Test with arrais. With arrais it is much faster
Option Explicit
Sub Subst()
With ThisWorkbook.Sheets("Sheet1")
Dim ArrayColumnD As Variant
ArrayColumnD = .Range("D1:D" & .Cells(.Rows.Count, 4).End(xlUp).Row)
Dim ArrayColumnG As Variant
ArrayColumnG = .Range("G1:G" & .Cells(.Rows.Count, 7).End(xlUp).Row)
Dim ID As String
Dim RowActual As Long
Dim RowTest As Long
For RowActual = 2 To UBound(ArrayColumnD)
If ArrayColumnG(RowActual, 1) = "Good" Then
ID = ArrayColumnD(RowActual, 1)
For RowTest = 2 To UBound(ArrayColumnD)
If ArrayColumnD(RowTest, 1) = ID Then
ArrayColumnG(RowTest, 1) = "Good"
End If
Next RowTest
End If
Next RowActual
.Range("G1:G" & .Cells(.Rows.Count, 7).End(xlUp).Row) = ArrayColumnG
End With
End Sub
I have the following code that I am using as a workaround instead of filtering the data as I have multiple criterias. I read somewhere that it is only possible to filter 2 criterias at a time?
The thing is that I have 5 - AB, DZ, RE, Z3, ZP - everything else should be deleted. So I am using the code below, which works fine, but having to deal with +30000 rows everytime I run the macro, it is extremely slow.
Is there anyway you can do this faster? I was thinking of just filtering each criteria at a time (creating 5 of the first of the below codes). But if there is anyway to do it faster, I would appreciate some help.
THE CODE I USE THAT IS SLOW:
' Step 13 - Filter and Delete All Except
' AB, DZ, RE, Z3, ZP in Column 6 - Type
Sub FilterDeleteType()
Dim rTable As Range, r As Range
Dim rDelete As Range
Set rDelete = Nothing
Dim v As Variant
Worksheets("Overdue Items").Activate
For Each r In Columns(6).Cells
v = r.Value
If v <> "Type" And v <> "AB" And v <> "DZ" And v <> "RE" And v <> "Z3" And v <> "ZP" Then
If rDelete Is Nothing Then
Set rDelete = r
Else
Set rDelete = Union(r, rDelete)
End If
End If
Next
If Not rDelete Is Nothing Then rDelete.EntireRow.Delete
End Sub
You can just look in hidden rows and check that column -
Sub test()
Dim lastrow As Integer
lastrow = Cells(Rows.Count, "A").End(xlUp).Row
Dim lastcol As Integer
lastcol = Cells(1, Columns.Count).End(xlToLeft).Column
'do your autofilter here
For i = 1 To lastrow
If Rows(i).Hidden = True Then
Range(Cells(i, 1), Cells(i, 5)).ClearContents
Range(Cells(i, 7), Cells(i, lastcol)).ClearContents
If Cells(i, 6) <> "AB" Or "DZ" Or "RE" Or "Z3" Or "ZP" Then
Cells(i, 6).ClearContents
End If
End If
Next
End Sub
So I managed to do exactly what my previous code was doing, just significantly faster. With the help from this post https://stackoverflow.com/a/22275522
What the code is doing is that it filter the values that I want (using an array), and then it will delete the hidden rows, meaning the rows that has NOT been filtered.
Sub FilterType()
Dim LRow As Long
Dim delRange As Range
Dim oRow As Range, rng As Range
Dim myRows As Range
Const Opt1 As String = "AB"
Const Opt2 As String = "DZ"
Const Opt3 As String = "RE"
Const Opt4 As String = "Z3"
Const Opt5 As String = "ZP"
On Error GoTo ErrHandler:
Sheets(1).Activate
With ThisWorkbook.Sheets(1)
'~~> Remove any filters
.AutoFilterMode = False
LRow = .Range("F" & .Rows.Count).End(xlUp).Row
With .Range("F1:F" & LRow)
.AutoFilter Field:=1, Criteria1:=Array(Opt1, Opt2, Opt3, Opt4, Opt5), Operator:=xlFilterValues
End With
With Sheets(1)
Set myRows = Intersect(.Range("F:F").EntireRow, .UsedRange)
If myRows Is Nothing Then Exit Sub
End With
For Each oRow In myRows.Columns(6).Cells
If oRow.EntireRow.Hidden Then
If rng Is Nothing Then
Set rng = oRow
Else
Set rng = Union(rng, oRow)
End If
End If
Next
ErrHandler:
'~~> Remove any filters
.AutoFilterMode = False
End With
If Not rng Is Nothing Then rng.EntireRow.Delete
End Sub
I need to delete the rows that:
- Does not have the Word "Get" into Column A, example: if A1 is Configuration Get, I should not delete; but if it is nFormat or anything else, I should delete.
- And for the rows which has the word "get" I also need to check if in Column C the value is 0, if it is not 0 I also should delete.
My function is working for sheet with a small number of rows, but the problem is, I really need to run it for a large number, let's say for 60000 rows. Could someone help me?
My function is:
Sub DeleteRows()
Dim c As Range
Dim ColumnA
Dim Lrow As Long
Dim Lastrow As Long
With Sheets("Sheet1") 'I'm using the Sheet1
.Select
Set ColumnA = ActiveSheet.UsedRange
Lastrow = .UsedRange.Rows(.UsedRange.Rows.Count).Row
For Lrow = Lastrow To 1 Step -1
Set ColumnA = Cells(Lrow, "A") 'I'm looking just in Column "A" for a Get
Set c = ColumnA.Find("Get", LookIn:=xlValues)
If Not c Is Nothing Then
' If the cell has a Get, it will look for a 0 in Column "C"
With .Cells(Lrow, "C")
If Not IsError(.Value) Then
' If the Value is not 0 the row will be delete.
If Not (.Value = 0) Then .EntireRow.Delete
End If
End With
Else
'If didn't find a "Get", it will delete the row
ColumnA.EntireRow.Delete
End If
Next Lrow
End With
End Sub
Try something like this which uses AutoFilter instead
It is the VBA equivalent of:
finding the first blank column
entering =OR(ISERROR(FIND("Get",$A1)),AND(NOT(ISERROR(FIND("Get",$A1))),$C1<>0)) in row 1 and copying down
deleting and TRUE results
cleaning up the working column
code
Sub KillEm()
Dim rng1 As Range, rng2 As Range, rng3 As Range
Set rng1 = Cells.Find("*", , xlValues, , xlByColumns, xlPrevious)
Set rng2 = Cells.Find("*", , xlValues, , xlByRows, xlPrevious)
Set rng3 = Range(Cells(rng2.Row, rng1.Column), Cells(1, rng1.Column))
Application.ScreenUpdating = False
With rng3.Offset(0, 1)
.FormulaR1C1 = "=OR(ISERROR(FIND(""Get"",RC1)),AND(NOT(ISERROR(FIND(""Get"",RC1))),RC3<>0))"
.AutoFilter Field:=1, Criteria1:="TRUE"
.Offset(1, 0).Resize(rng3.Rows.Count - 1, 1).EntireRow.Delete
.EntireColumn.Delete
End With
Application.ScreenUpdating = True
End Sub
I did like this for, in this case sheet7, and it worked:
Application.ScreenUpdating = False
With Sheet7
r = 1
Do While r <= LastRow
If IsError(.Cells(r, 1)) Then
.Rows(r).Delete
LastRow = LastRow - 1
Else
If InStr(.Cells(r, 1), "Get") = 0 Then
.Rows(r).Delete
LastRow = LastRow - 1
Else
r = r + 1
End If
End If
Loop
End With
Application.ScreenUpdating = True
I'm close to figuring out the correct way to do this but I'm all searched out. I'm trying to search a column (Asset Tag) that holds various garbage but want to keep entire rows that start with AA as this confirms the row is actually an Asset Tag.
The code below gives me an "Object Required" error, and I believe I might not be correctly telling it to look at cell values with the"If rng.Cells(i) <> Left(cell.Value, 2) = "AA" Then" statement. Can someone point me in the right direction of what I need to do?
Sub DeleteRows()
Dim rng As Range
Dim i As Double, counter As Double
Set rng = Range("C:C")
i = 1
For counter = 1 To rng.Rows.count
If rng.Cells(i) <> Left(cell.Value, 2) = "AA" Then
rng.Cells(i).EntireRow.Delete
Else
i = i + 1
End If
Next
End Sub
Thanks!
If you're deleting rows then you should always work from the bottom up:
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
Given your code took 5 minutes I suggest you try this AutoFilter approach. It uses a working column (the first unused column part the usedrange) to run a case sensitive text for the first two letters of column C cells being "AA". If not an autofilter deletes these rows.
Sub KillnonAA()
Dim ws As Worksheet
Dim rng1 As Range
Dim lRow As Long
Dim lCol As Long
Set ws = ActiveSheet
'in case the sheet is blank
On Error Resume Next
lRow = ws.Cells.Find("*", , xlValues, , xlByRows, xlPrevious).Row
lCol = ws.Cells.Find("*", , xlValues, , xlByColumns, xlPrevious).Column
On Error GoTo 0
If lRow = 0 Then Exit Sub
Set rng1 = ws.Range(ws.Cells(1, lCol + 1), ws.Cells(lRow, lCol + 1))
Application.ScreenUpdating = False
Rows(1).Insert
With rng1.Offset(0, 1)
.FormulaR1C1 = "=EXACT(LEFT(RC3,2),""AA"")"
.AutoFilter Field:=1, Criteria1:="FALSE"
.EntireRow.Delete
'in case all records were deleted
On Error Resume Next
.EntireColumn.Delete
On Error GoTo 0
End With
Application.ScreenUpdating = True
End Sub