Selection based on finding 2 different words in 2 columns - excel

I would like to do the following using Excel VBA:
1) look for a certain word_1 within a column;
2) if word_1 was found in step (1), go one column to the right and look for another word which is called word_2. If word_2 was found as well, delete the entire row.
If on the other hand, word_2 was not found, the row does not have to be deleted.
The general idea is to search for multiple words in one column and if they are found, also double-check (for safety) if certain affiliated words are in column 2. Only then the entire rows should be deleted.
I made the following little example for testing:
Col1 Col2
xxx xxx
xxx xxx
xxx xxx
findme acg
xxx xxx
findme xxx
In this example I am searching for the word "findme" in column 1 and for the associated word "acg" in column 2. As you can see, row 4 would have to be deleted because both words occur in one row, as opposed to e.g. row 6, where this is not the case.
My final code:
Sub xxx()
Dim aCell As Range, bCell As Range, aSave As String
Dim fndOne As String, fndTwo As String
fndOne = "findme"
fndTwo = "acg"
Dim ws As Worksheet: Set ws = ActiveWorkbook.ActiveSheet
Application.DisplayAlerts = False
Application.ScreenUpdating = False
With ws
Set aCell = .Columns(1).Find(What:=fndOne, LookIn:=xlValues, _
lookat:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then
aSave = aCell.Address
Do
If LCase(.Cells(aCell.row, 2).Value) Like Chr(42) & fndTwo & Chr(42) Then
If bCell Is Nothing Then
Set bCell = .Range("A" & aCell.row)
Else
Set bCell = Union(bCell, .Range("A" & aCell.row))
End If
End If
Set aCell = .Columns(1).FindNext(After:=aCell)
Loop Until aCell.Address = aSave
End If
Set aCell = Nothing
If Not bCell Is Nothing Then bCell.EntireRow.Delete
End With
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub

If you used the Range.Find method and Range.FindNext method, deleting as you go and checking for matching records after each deletion, you should be able to loop through the possibilities quickly.
'delete rows as they are found
Sub delTwofers()
Dim rw As Long, n As Long, cnt As Long, rng As Range
Dim v As Long, sALLTERMs As String, vPAIRs As Variant, vTERMs As Variant
On Error GoTo bm_SafeExit
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
Debug.Print Timer
sALLTERMs = "aa;bb|cc;dd|ee;ff"
With Worksheets("Sheet1") 'set this worksheet reference properly!
vPAIRs = Split(LCase(sALLTERMs), Chr(124))
For v = LBound(vPAIRs) To UBound(vPAIRs)
vTERMs = Split(vPAIRs(v), Chr(59))
cnt = Application.CountIfs(.Columns(1), Chr(42) & vTERMs(0) & Chr(42), .Columns(2), Chr(42) & vTERMs(1) & Chr(42))
rw = 1
For n = 1 To cnt
rw = .Columns(1).Find(what:=vTERMs(0), lookat:=xlPart, _
after:=.Columns(1).Cells(rw + (rw <> 1)), MatchCase:=False).Row
Do While True
If LCase(.Cells(rw, 2).Value2) Like Chr(42) & vTERMs(1) & Chr(42) Then
.Rows(rw).Delete
Exit Do
Else
rw = .Columns(1).FindNext(after:=.Cells(rw, 1)).Row
End If
Loop
Next n
Next v
End With
Debug.Print Timer
bm_SafeExit:
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
'collect rows with Union, delete them all at once
Sub delTwofers2()
Dim rw As Long, n As Long, cnt As Long, rng As Range
Dim v As Long, sALLTERMs As String, vPAIRs As Variant, vTERMs As Variant
On Error GoTo bm_SafeExit
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
Debug.Print Timer
sALLTERMs = "aa;bb|cc;dd|ee;ff"
With Worksheets("Sheet1") 'set this worksheet reference properly!
vPAIRs = Split(LCase(sALLTERMs), Chr(124))
For v = LBound(vPAIRs) To UBound(vPAIRs)
vTERMs = Split(vPAIRs(v), Chr(59))
cnt = Application.CountIfs(.Columns(1), Chr(42) & vTERMs(0) & Chr(42), .Columns(2), Chr(42) & vTERMs(1) & Chr(42))
rw = 1
For n = 1 To cnt
rw = .Columns(1).Find(what:=vTERMs(0), lookat:=xlPart, _
after:=.Columns(1).Cells(rw), MatchCase:=False).Row
Do While True
If LCase(.Cells(rw, 2).Value2) Like Chr(42) & vTERMs(1) & Chr(42) Then
If rng Is Nothing Then
Set rng = .Cells(rw, 1)
Else
Set rng = Union(rng, .Cells(rw, 1))
End If
Exit Do
Else
rw = .Columns(1).FindNext(after:=.Cells(rw, 1)).Row
End If
Loop
Next n
Next v
End With
Debug.Print Timer 'check timer before deleting discontiguous rows
If Not rng Is Nothing Then _
rng.EntireRow.Delete
Debug.Print Timer
bm_SafeExit:
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
By first checking to make sure there is something to delete, some error control can be avoided; you only need to find the entry for the double matching criteria that you know exists.
Addendum: Deleting a collection of discontiguous rows is time consuming. The second routine (delTwofers2) above was 5% slower that the one that deleted rows as they were found. 25,000 values, 755 random deletions - 3.60 seconds for the first; 3.75 seconds for the latter.

This code applies a filter to the first two columns of the used range using your criteria. It then deletes the visible rows:
Sub DeleteSelected()
Dim RangeToFilter As Excel.Range
Set RangeToFilter = ActiveSheet.UsedRange
With RangeToFilter
.AutoFilter Field:=1, Criteria1:="find me"
.AutoFilter Field:=2, Criteria1:="access granted"
.SpecialCells(xlCellTypeVisible).EntireRow.Delete Shift:=xlUp
End With
End Sub

Related

Finding key words in Excel with VBA

The file I am working on is picking the selected words from all the comments, colors them and segregates them into the dedicated tabs.
All keywords have been coded into the macro itself. Instead of writing the keywords to the macro, I want to tell the macro the keywords are located in an array in an excel sheet so everybody can use the file according to their needs.
When I made below changes for keywords to an array, I am getting below error on the screenshot that I do not know why.
Satellite:
KeyW = Array("Satellite", "image", "blacks out", "resolution")
Satellite:
KeyW = Array(Worksheets("MAIN").Range("N5:N15"))
The code below was not written by me. I just made some modifications.
Error that I am getting:
runtime error 13, Type mismatch
when I click debug it shows this yellow line
Sub sort()
Dim KeyW()
Dim cnt_Rows As Long, cnt_Columns As Long, curr_Row As Long, i As Long, x As Long
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Sheets(Array("Television", "Satellite", "News", "Sports", "Movies", "Key2", "Key3", "Error", "Commercial", "Key4", "TV", "Key5", "Key6", "Signal", "Key1", "Key7", "Design", "Hardware")).Select
Satellite:
KeyW = Array("Satellite", "image", "blacks out", "resolution")
KeyWLen = UBound(KeyW, 1)
j = 2
For i = 0 To KeyWLen
With Worksheets(1).Range("c4:e7000")
Set c = .Find(KeyW(i), LookIn:=xlValues, LookAt:=xlPart)
If Not c Is Nothing Then
firstAddress = c.Address
Do
Sheets("Satellite").Range("b" & j).Value = Worksheets(1).Range("a" & c.Row).Value
Worksheets(1).Range(c.Address).Copy
Sheets("Satellite").Activate
Range("a" & j).Select
Selection.PasteSpecial Paste:=xlPasteAllUsingSourceTheme, Operation:=xlNone _
, SkipBlanks:=False, Transpose:=False
Range("a" & j).Select
WordPos = 1
StartPos = 1
SearchStr = KeyW(i)
While WordPos <> 0
WordPos = InStr(StartPos + 1, Range("a" & j).Value, SearchStr, 1)
If WordPos > 0 _
Then
With ActiveCell.Characters(Start:=WordPos, Length:=Len(SearchStr)).Font
.FontStyle = "Bold"
.Color = -16727809
End With
StartPos = WordPos
End If
Wend
Worksheets(1).Activate
j = j + 1
Set c = .FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
End With
Next i
I'd start by splitting out some of the logic into standalone methods, and calling them from your main code: this makes it easier to see what's going on and allows some re-use of your code later on.
For example:
Sub sort()
Dim wb As Workbook
Dim txt As String, allCells As Collection, c As Range, w As Range, rngDest As Range
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Set wb = ThisWorkbook
'(removed sheet selection code - not needed here)
Set rngDest = wb.Worksheets("Satellite").Range("A2") 'start listing matches here
For Each w In wb.Worksheets("MAIN").Range("N5:N15").Cells 'loop over possible search terms
txt = Trim(w.Value)
If Len(txt) > 0 Then
Set allCells = FindAll(wb.Worksheets(1).Range("c4:e7000"), txt) 'get all matches
For Each c In allCells
c.Copy rngDest 'copy matched cell
BoldWord rngDest, txt 'bold matched text
rngDest.Offset(0, 1) = _
c.EntireRow.Columns("A").Value 'copy colA from matched cell
Set rngDest = rngDest.Offset(1) 'next result row
Next c
End If
Next w
End Sub
'return a Collection of all cells in `rng` which contain `txt`
Public Function FindAll(rng As Range, txt As String) As Collection
Dim rv As New Collection, f As Range
Dim addr As String
Set f = rng.Find(what:=txt, after:=rng.Cells(rng.Cells.Count), _
LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False)
If Not f Is Nothing Then addr = f.Address()
Do Until f Is Nothing
rv.Add f
Set f = rng.FindNext(after:=f)
If f.Address() = addr Then Exit Do
Loop
Set FindAll = rv
End Function
'Bold all instances of `wrd` in cell `c`
Sub BoldWord(c As Range, txt As String)
Dim pos As Long, start As Long
start = 1
Do
pos = InStr(start, c.Value, txt, vbTextCompare)
If pos = 0 Then Exit Do
With c.Characters(pos, Len(txt))
.Font.Bold = True
.Font.Color = vbRed
End With
start = pos + Len(txt)
Loop
End Sub

Excel VBA cut and paste a dynamic range of cells [duplicate]

I would like to delete the empty rows my ERP Quotation generates. I'm trying to go through the document (A1:Z50) and for each row where there is no data in the cells (A1-B1...Z1 = empty, A5-B5...Z5 = empty) I want to delete them.
I found this, but can't seem to configure it for me.
On Error Resume Next
Worksheet.Columns("A:A").SpecialCells(xlCellTypeBlanks).EntireRow.Delete
On Error GoTo 0
How about
sub foo()
dim r As Range, rows As Long, i As Long
Set r = ActiveSheet.Range("A1:Z50")
rows = r.rows.Count
For i = rows To 1 Step (-1)
If WorksheetFunction.CountA(r.rows(i)) = 0 Then r.rows(i).Delete
Next
End Sub
Try this
Option Explicit
Sub Sample()
Dim i As Long
Dim DelRange As Range
On Error GoTo Whoa
Application.ScreenUpdating = False
For i = 1 To 50
If Application.WorksheetFunction.CountA(Range("A" & i & ":" & "Z" & i)) = 0 Then
If DelRange Is Nothing Then
Set DelRange = Range("A" & i & ":" & "Z" & i)
Else
Set DelRange = Union(DelRange, Range("A" & i & ":" & "Z" & i))
End If
End If
Next i
If Not DelRange Is Nothing Then DelRange.Delete shift:=xlUp
LetsContinue:
Application.ScreenUpdating = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume LetsContinue
End Sub
IF you want to delete the entire row then use this code
Option Explicit
Sub Sample()
Dim i As Long
Dim DelRange As Range
On Error GoTo Whoa
Application.ScreenUpdating = False
For i = 1 To 50
If Application.WorksheetFunction.CountA(Range("A" & i & ":" & "Z" & i)) = 0 Then
If DelRange Is Nothing Then
Set DelRange = Rows(i)
Else
Set DelRange = Union(DelRange, Rows(i))
End If
End If
Next i
If Not DelRange Is Nothing Then DelRange.Delete shift:=xlUp
LetsContinue:
Application.ScreenUpdating = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume LetsContinue
End Sub
I know I am late to the party, but here is some code I wrote/use to do the job.
Sub DeleteERows()
Sheets("Sheet1").Select
Range("a2:A15000").Select
Selection.SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End Sub
for those who are intersted to remove "empty" and "blank" rows ( Ctrl + Shift + End going deep down of your worksheet ) .. here is my code.
It will find the last "real"row in each sheet and delete the remaining blank rows.
Function XLBlank()
For Each sh In ActiveWorkbook.Worksheets
sh.Activate
Cells(1, 1).Select
lRow = Cells.Find(What:="*", _
After:=Range("A1"), _
LookAt:=xlPart, _
LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
MatchCase:=False).Row
Range("A" & lRow + 1, Range("A1").SpecialCells(xlCellTypeLastCell).Address).Select
On Error Resume Next
Selection.EntireRow.SpecialCells(xlBlanks).EntireRow.Delete
Cells(1, 1).Select
Next
ActiveWorkbook.Save
ActiveWorkbook.Worksheets(1).Activate
End Function
Open VBA ( ALT + F11 ), Insert -> Module,
Copy past my code and launch it with F5.
Et voila :D
I have another one for the case when you want to delete only rows which are complete empty, but not single empty cells. It also works outside of Excel e.g. on accessing Excel by Access-VBA or VB6.
Public Sub DeleteEmptyRows(Sheet As Excel.Worksheet)
Dim Row As Range
Dim Index As Long
Dim Count As Long
If Sheet Is Nothing Then Exit Sub
' We are iterating across a collection where we delete elements on the way.
' So its safe to iterate from the end to the beginning to avoid index confusion.
For Index = Sheet.UsedRange.Rows.Count To 1 Step -1
Set Row = Sheet.UsedRange.Rows(Index)
' This construct is necessary because SpecialCells(xlCellTypeBlanks)
' always throws runtime errors if it doesn't find any empty cell.
Count = 0
On Error Resume Next
Count = Row.SpecialCells(xlCellTypeBlanks).Count
On Error GoTo 0
If Count = Row.Cells.Count Then Row.Delete xlUp
Next
End Sub
To make Alex K's answer slightly more dynamic you could use the code below:
Sub DeleteBlankRows()
Dim wks As Worksheet
Dim lngLastRow As Long, lngLastCol As Long, lngIdx As Long, _
lngColCounter As Long
Dim blnAllBlank As Boolean
Dim UserInputSheet As String
UserInputSheet = Application.InputBox("Enter the name of the sheet which you wish to remove empty rows from")
Set wks = Worksheets(UserInputSheet)
With wks
'Now that our sheet is defined, we'll find the last row and last column
lngLastRow = .Cells.Find(What:="*", LookIn:=xlFormulas, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
lngLastCol = .Cells.Find(What:="*", LookIn:=xlFormulas, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlPrevious).Column
'Since we need to delete rows, we start from the bottom and move up
For lngIdx = lngLastRow To 1 Step -1
'Start by setting a flag to immediately stop checking
'if a cell is NOT blank and initializing the column counter
blnAllBlank = True
lngColCounter = 2
'Check cells from left to right while the flag is True
'and the we are within the farthest-right column
While blnAllBlank And lngColCounter <= lngLastCol
'If the cell is NOT blank, trip the flag and exit the loop
If .Cells(lngIdx, lngColCounter) <> "" Then
blnAllBlank = False
Else
lngColCounter = lngColCounter + 1
End If
Wend
'Delete the row if the blnBlank variable is True
If blnAllBlank Then
.rows(lngIdx).delete
End If
Next lngIdx
End With
MsgBox "Blank rows have been deleted."
End Sub
This was sourced from this website and then slightly adapted to allow the user to choose which worksheet they want to empty rows removed from.
In order to have the On Error Resume function work you must declare the workbook and worksheet values as such
On Error Resume Next
ActiveWorkbook.Worksheets("Sheet Name").Columns("A:A").SpecialCells(xlCellTypeBlanks).EntireRow.Delete
On Error GoTo 0
I had the same issue and this eliminated all the empty rows without the need to implement a For loop.
This worked great for me (you can adjust lastrow and lastcol as needed):
Sub delete_rows_blank2()
t = 1
lastrow = ActiveSheet.UsedRange.Rows.Count
lastcol = ActiveSheet.UsedRange.Columns.Count
Do Until t = lastrow
For j = 1 To lastcol
'This only checks the first column because the "Else" statement below will skip to the next row if the first column has content.
If Cells(t, j) = "" Then
j = j + 1
If j = lastcol Then
Rows(t).Delete
t = t + 1
End If
Else
'Note that doing this row skip, may prevent user from checking other columns for blanks.
t = t + 1
End If
Next
Loop
End Sub
Here is the quickest way to Delete all blank Rows ( based on one Columns )
Dim lstRow as integet, ws as worksheet
Set ws = ThisWorkbook.Sheets("NameOfSheet")
With ws
lstRow = .Cells(Rows.Count, "B").End(xlUp).Row ' Or Rows.Count "B", "C" or "A" depends
.Range("A1:E" & lstRow).SpecialCells(xlCellTypeBlanks).EntireRow.Delete
End with

Count of distinct values from filtered column

I have one Excel sheet with 6000 rows. I need to delete entire rows if distinct values are less than, say, three in one particular column.
Per below example:
In column-A with the list of colours and in column-B with names.
If I filter any 'name in column-B and in column-A, if less than three distinct values = true then entire row should be deleted.
Rows with name- Chary should be deleted.
A B
Color Employee
Red Dev
blue Dev
blue Dev
Red Dev
black Dev
Red Dev
Red Chary
blue Chary
blue Chary
Red Chary
Red Chary
Red Chary
With my code:
First I filter name in column-B then paste the filtered data new workbook and there I will remove duplicates from column-A then will get the unique count.
If the unique count is less than 3 then activate the main sheet and will delete filtered rows and loop to next name.
Sub Del_lessthan_5folois()
Application.ScreenUpdating = False
Application.DisplayAlerts = False
t = Now()
Set wb = ActiveWorkbook
Sheets("VALID ARNS").Activate
iCol = 2 '### criteria column
Set ws = Sheets("VALID ARNS")
Sheets("VALID ARNS").Activate
Set rnglast = Columns(iCol).Find("*", Cells(1, iCol), , , xlByColumns, xlPrevious)
ws.Columns(iCol).AdvancedFilter Action:=xlFilterInPlace, Unique:=True
Set rngUnique = Range(Cells(2, iCol), rnglast).SpecialCells(xlCellTypeVisible)
Workbooks.Add
Set newb = ActiveWorkbook
For Each strItem In rngUnique
If strItem <> "" Then
ws.UsedRange.AutoFilter Field:=iCol, Criteria1:=strItem.Value
newb.Activate
ws.UsedRange.SpecialCells(xlCellTypeVisible).Copy Destination:=[A1]
Application.CutCopyMode = False
Cells.EntireColumn.AutoFit
Dim uniq As Range
Set uniq = Range("A1:S" & Range("A" & Rows.Count).End(xlUp).Row)
uniq.RemoveDuplicates Columns:=7, Header:=xlYes
LastRow = ActiveSheet.UsedRange.Rows.Count
Cells.Delete Shift:=xlUp
Range("A1").Select
wb.Activate
If LastRow < "3" Then
ActiveSheet.AutoFilter.Range.Offset(1,0).Rows.SpecialCells(xlCellTypeVisible).Delete (xlShiftUp)
End If
End If
Next
ws.ShowAllData
MsgBox "The entire process took! " & Format(Now() - t, "hh:mm:ss") & " Minutes"
ActiveSheet.AutoFilterMode = False
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
My code works in step by step debug mode but when run it skips a lot of rows.
Can this be related to more than 6000 rows?
How do I get the count of distinct values in Column-A when filtered in Column-B?
It's not exactly the same code that you posted as I had some troubles with it, but here's an alternative solution. I simply copy the data into another sheet (please add sheet called "Results" before you run my code), add two more columns with formulas (these will check if a given "Employee" should be deleted), filter on "TRUE" and then delete relevant rows.
From what I tested such solution seems to be faster than applying Advanced Filters, checking for unique values and then looping through the whole dataset. I hope it will work fine for your setup.
Here's the code:
Sub DeleteRows()
Dim t As Variant
Dim iCol As Long, lngLastRow As Long
Dim wsOrig As Worksheet, wsNew As Worksheet
With Application
.ScreenUpdating = False
.DisplayAlerts = False
End With
t = Now()
Set wsOrig = Sheets("VALID ARNS")
Set wsNew = Sheets("Results")
iCol = 2 '### criteria column
With wsOrig
lngLastRow = .Columns(iCol).Find("*", Cells(1, iCol), , , xlByColumns, xlPrevious).Row
'copy into Results sheet
.Range("A1:B" & lngLastRow).Copy wsNew.Range("A1")
With wsNew
'add formulas
.Range("C1:D1").Value = VBA.Array("Instance", "Delete?")
.Range("C2:C" & lngLastRow).Formula = "=COUNTIFS($A$2:A2,A2,$B$2:B2,B2)"
.Range("D2:D" & lngLastRow).Formula = "=SUMIFS($C$2:$C$" & lngLastRow & ",$B$2:$B$" & lngLastRow & ",B2,$C$2:$C$" & lngLastRow & ",1)<3"
'delete when column D = TRUE
.Range("A1:D" & lngLastRow).AutoFilter Field:=4, Criteria1:="TRUE"
.Range("D2:D" & lngLastRow).SpecialCells(xlCellTypeVisible).Rows.Delete
'clear
.Range("A1:B" & lngLastRow).AutoFilter
.Range("C:D").Clear
End With
End With
MsgBox "The entire process took! " & Format(Now() - t, "hh:mm:ss") & " Minutes"
With Application
.ScreenUpdating = True
.DisplayAlerts = True
End With
End Sub
"VALID ARNS" sheet:
"Results" sheet (after running the code):
Edit:
Another option, using Scripting.Dictionary functionality:
Public Function getUnique(ByVal rngVals As Excel.Range) As Variant()
Dim objDictionary As Object
Dim rngRow As Excel.Range
Dim rngCell As Excel.Range
Dim strKey As String
Set objDictionary = CreateObject("Scripting.Dictionary")
For Each rngRow In rngVals.Rows
For Each rngCell In rngRow.Cells
strKey = strKey & "||" & rngCell.Text
Next rngCell
With objDictionary
If Not .Exists(Key:=Mid$(strKey, 3)) Then
Call .Add(Key:=Mid$(strKey, 3), Item:=Mid$(strKey, 3))
End If
End With
strKey = ""
Next rngRow
getUnique = objDictionary.Keys
Set rngVals = Nothing
Set rngRow = Nothing
Set rngCell = Nothing
End Function
Public Sub CountUnique()
Dim rngVals As Excel.Range
Dim varUnique() As Variant
Dim rngCell As Excel.Range
Dim varTemp As Variant
Set rngVals = Sheet3.Range("A2:B13").SpecialCells(12)
varUnique = getUnique(rngVals)
For Each rngCell In rngVals.Columns(2).Cells
varTemp = Filter(varUnique, rngCell.Text, True)
Debug.Print rngCell.Text, UBound(varTemp) - LBound(varTemp) + 1
Erase varTemp
Next rngCell
Set rngVals = Nothing
Set rngCell = Nothing
Erase varUnique
End Sub

Need help to reduce the execution time for the code below

Below code compares 1500 rows with about 14 columns . It takes around 30 min to execute them . Is there any way , I could reduce the code by altering the code below .I like to get all your expert opinion.
The code makes the following function
Run through all records in Old Sheet.
if found in NEW sheet , Do nothing
if not found in NEW sheet , Delete it from OLD sheet
Option Explicit
Function UpdateOLD() As Long
' This Sub will do the Following Update
' Run through all records in OLD
' if found in NEW ---> Do nothing
' if not found in NEW ----> Delete it from OLD.
'
Dim WSO As Worksheet
Dim WSN As Worksheet
Dim MaxRowO As Long, MaxRowN As Long, I As Long, J As Long, lDel As Long
Dim sJob As String, sOps As String, sFirstAddress As String
Dim cCell As Range
Dim bNotFound As Boolean
'---> Disable Events
With Application
.EnableEvents = False
.DisplayAlerts = False
.ScreenUpdating = False
End With
'---> Set Variables
Set WSO = Sheets("Steps")
Set WSN = Sheets("Interface")
MaxRowO = WSO.Range("A" & WSO.Rows.Count).End(xlUp).Row
MaxRowN = WSN.Range("C" & WSN.Rows.Count).End(xlUp).Row
WSO.Range("N2:N" & MaxRowO).ClearContents
'---> Loop thruough all rows in sheet New
For I = MaxRowO To 2 Step -1
bNotFound = False
sJob = WSO.Cells(I, "B")
sOps = WSO.Cells(I, "C")
Set cCell = WSN.Range("D6:D" & MaxRowN).Find(what:=sJob, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False)
If Not cCell Is Nothing Then
bNotFound = True
sFirstAddress = cCell.Address
Do
'---> Check to See if Ops if found for that Job
If WSN.Cells(cCell.Row, "E") = sOps Then
bNotFound = False
Exit Do
End If
Set cCell = WSN.Range("D6:D" & MaxRowN).FindNext(cCell)
Loop While Not cCell Is Nothing And cCell.Address <> sFirstAddress
Else
bNotFound = True
End If
'---> Del Record from OLD if Not Found
If bNotFound Then
WSO.Range(I & ":" & I).EntireRow.Delete
'WSO.Range("N" & I) = sJob & " " & sOps & " Deleted as NOT found in NEW"
lDel = lDel + 1
End If
Next I
'---> Enable Events
With Application
.EnableEvents = True
.DisplayAlerts = True
.ScreenUpdating = True
End With
UpdateOLD = lDel
End Function
Welcome to SO.Tried your code with around 2000 rows with around 10% mismatch and it take only few seconds. may be the file have some other issues. However one way to speed up (about 1/2 the time taken in my trial) is to add all the bNotFound cells in a union of range and delete the EntireRow of the range in one shot after finishing the loop.
Changes in Code:
Dim Rng As Range 'Add in Declare section
'
'
'
'
For I = 2 To MaxRowO 'No need to loop backward
'
'
'
'
If bNotFound Then ' Only add to Union of ranges
If Rng Is Nothing Then
Set Rng = WSO.Range("A" & I)
Else
Set Rng = Union(Rng, WSO.Range("A" & I))
lDel = lDel + 1
End If
End If
Next I
If Not Rng Is Nothing Then Rng.EntireRow.Delete ' delete in one shot

Hiding Rows if Cell in a Column Contains Certain Text or Autofiltering a Single Field with 4 Criteria

I have a sheet with columns A through M, containing a table including all rows and columns. If, in column E, a cell contains the string(s) "Drive", "Inactivity", or "Halt" then I want the row to be hidden. If, in column E, a cell does not contain the string "UF_", then I want it to be hidden.
I have tried several things and have looked in many places. Here is some code that I have tried:
Try 1 (takes wayyyy to long):
With ActiveSheet
loopct = 2
While loopct < count1
DoEvents
Application.StatusBar = "Making Table " & loopct
txtrmv1 = "Drive"
txtrmv2 = "Inactivity"
txtrmv3 = "Halt"
txtkp = "UF_"
celltxt = .Range("E" & loopct).Value
If InStr(1, celltxt, txtrmv1, vbTextCompare) Then
.Range("E" & loopct).EntireRow.Hidden = True
End If
If InStr(1, celltxt, txtrmv2, vbTextCompare) Then
.Range("E" & loopct).EntireRow.Hidden = True
End If
If InStr(1, celltxt, txtrmv3, vbTextCompare) Then
.Range("E" & loopct).EntireRow.Hidden = True
End If
If InStr(1, celltxt, txtkp, vbTextCompare) Then
.Range("E" & loopct).EntireRow.Hidden = False
Else
.Range("E" & loopct).EntireRow.Hidden = True
End If
loopct = loopct + 1
Wend
End With
Try 2 (runs but accomplishes nothing):
Private Sub HideDrive(ByVal count1 As Long)
Dim ws As Worksheet
Dim rng As Range, aCell As Range, bCell As Range
Set ws = ActiveWorkbook.Sheets("Sheet1")
With ws
Set rng = .Range("E2:E" & CStr(count1))
Set aCell = rng.Find(What:="Drive", LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=True, SearchFormat:=False)
If Not aCell Is Nothing Then
Set bCell = aCell
Do
aCell.EntireRow.Hidden = True
Set aCell = rng.FindNext(After:=aCell)
Loop While aCell Is Nothing And aCell.Address <> bCell
End If
End With
End Sub
Here is what I was using when I only had one criteria to check for (obviously my sitation has changed):
ActiveWorkbook.ActiveSheet.ListObjects(tblnm).Range.AutoFilter Field:=5, Criteria1:= "=*UF_*"
What can I do to accomplish what I want? I haven't been able to get autofilter to work with more than two criteria. Please let me know!
I couldn't debug and run the other answer given, so I continued working and solved it myself.
Instead of trying to hide each word I didn't want all together, I hid them individually and then called a hidden row deleting function each time.
ActiveSheet.ListObjects.Add(xlSrcRange, Range(DataArea1), , xlYes).Name = _
tblnm
'insert if statement here to change filters based upon area
ActiveWorkbook.ActiveSheet.ListObjects(tblnm).Range.AutoFilter Field:=5, Criteria1:="=*UF_*"
Call RhidRow2(count4)
ActiveSheet.ListObjects(tblnm).Range.AutoFilter Field:=5, Criteria1:="<>*Drive*"
Call RhidRow2(count4)
ActiveSheet.ListObjects(tblnm).Range.AutoFilter Field:=5, Criteria1:="<>*Inactivity*"
Call RhidRow2(count4)
ActiveSheet.ListObjects(tblnm).Range.AutoFilter Field:=5, Criteria1:="<>*Halt*"
Call RhidRow2(count4)
ActiveSheet.ListObjects(tblnm).Range.AutoFilter Field:=8, Criteria1:="<>#VALUE!"
Call RhidRow2(count4)
Here is the hidden row deleter:
Private Sub RhidRow2(ByVal count4 As Long)
Dim count1 As Long 'counters to be used
Dim ws As Worksheet
Dim rngVis As Range
Dim rngDel As Range
Set ws = ActiveSheet
On Error Resume Next
Set rngVis = ws.Range("A2:A" & count4).SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If rngVis Is Nothing Then
ws.Range("Z1").Value = 1
Else
For count1 = count4 To 2 Step -1
If ws.Rows(count1).Hidden = True Then
If rngDel Is Nothing Then
Set rngDel = ws.Rows(count1)
Else
Set rngDel = Union(rngDel, ws.Rows(count1))
End If
End If
Next count1
If Not rngDel Is Nothing Then
Application.DisplayAlerts = False
Intersect(rngDel, rngDel.ListObject.DataBodyRange).Delete 'if row is hidden, delete
Application.DisplayAlerts = True
End If
End If
End Sub
This works better and faster than anything else I had tried or was suggested.
You might be hiding many times. This is better:
If InStr(1, celltxt, txtrmv1, vbTextCompare) > 0 Or _
InStr(1, celltxt, txtrmv2, vbTextCompare) > 0 Or _
InStr(1, celltxt, txtrmv3, vbTextCompare) > 0 Or _
InStr(1, celltxt, txtkp, vbTextCompare) = 0 Then
.Range("E" & loopct).EntireRow.Hidden = True
End If
EDIT: This should be a SIGNIFICANT speedup - it hides 10 rows at a time:
(added Next iRow also)
Option Explicit
Dim ws As Worksheet
Sub Sub1()
Dim iRow&, Count1&, txtrmv1, txtrmv2$, txtrmv3$, txtkp$, celltxt$
Set ws = ActiveWorkbook.Sheets("Sheet1")
Count1 = 65000 ' ??
txtrmv1 = "Drive"
txtrmv2 = "Inactivity"
txtrmv3 = "Halt"
txtkp = "UF_"
For iRow = 2 To Count1
DoEvents
Application.StatusBar = "Making Table " & iRow
celltxt = ws.Range("E" & iRow).Value
If InStr(1, celltxt, txtrmv1, vbTextCompare) > 0 Or _
InStr(1, celltxt, txtrmv2, vbTextCompare) > 0 Or _
InStr(1, celltxt, txtrmv3, vbTextCompare) > 0 Or _
InStr(1, celltxt, txtkp, vbTextCompare) = 0 Then
Call hideSub(iRow) '
End If
Next iRow ' thank you, tannmann357
Call hideSub(0) ' flush
End Sub
Sub hideSub(hideRow&) ' hides 10 rows at a time
Static a1&(10), na1&
Dim i1&, zRange As Range
If hideRow = 0 Then ' finish;end;flush
For i1 = 1 To na1
ws.Rows(a1(i1)).Hidden = True
Next i1
na1 = 0
Else ' store row in array a1
na1 = na1 + 1
a1(na1) = hideRow
If na1 = 10 Then ' hide 10 rows
Set zRange = Union( _
Rows(a1(1)), Rows(a1(2)), Rows(a1(3)), Rows(a1(4)), Rows(a1(5)), _
Rows(a1(6)), Rows(a1(7)), Rows(a1(8)), Rows(a1(9)), Rows(a1(10)))
'Debug.Print zRange.Address
' this works but the syntax seems strange -- help me out
ws.Range(zRange.Address).Rows.Hidden = True
na1 = 0
End If
End If
End Sub
EDIT: for my benefit:
replace
' this works but the syntax seems strange -- help me out
ws.Range(zRange.Address).Rows.Hidden = True
with
ws.Range(zRange).Rows.Hidden = True

Resources