VBA for cut Data for certain Keywords - vba

I want cut data from A1:D10000 for certain keyword. Example if have any string with "Release Date:\n "it will be cut & paste in corresponding cell in F column.
I mean if any string have with "Release Date:\n " in C21 then it will be cut & paste in F21
I have a code & it works fine. But problem is it needs huge time to complete than i expected. Any better code as if it runs quickly?
My Code:
Sub Macro87()
For Repeat = 1 To 10000
Dim found As Range
Sheets("part15").Select
Range("A1").Select
Columns("A:D").EntireColumn.Select
Set found = Selection.Find(What:="Release Date:\n ", After:=ActiveCell, LookIn:= _
xlValues, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext _
, MatchCase:=False, SearchFormat:=False)
If Not found Is Nothing Then
found.Select
ActiveCell.Select
Selection.Cut
Range("F" & (ActiveCell.Row)).Select
ActiveSheet.Paste
Set found = Nothing
End If
Next
End Sub

Firstly, stop going from 1 to 10,000 - you don't need to repeat the action so much when you can use .FindNext instead. Secondly, avoid like the plague the use of .Select.
Dim ws as Worksheet
Set ws = ThisWorkbook.Worksheets("part15")
Dim lastRow
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
Dim found As Range
With ws.Range("A:D")
Set found = .Find(What:="Release Date:\n ", After:=ActiveCell, LookIn:= _
xlValues, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext _
, MatchCase:=False, SearchFormat:=False)
If Not found Is Nothing Then
firstAddress = found.Address
Do
ws.Range("F" & found.Row).Value = found.Value
set found = .FindNext(found)
Loop While Not found Is Nothing And found.Address <> firstAddress
End If
End With
End Sub

As Dave mentioned, much of your slow down is going to come from the repeated select statements in your code. VBA usually does not need to select a range in order to work with the data therein. The following should speed things up for you quite a bit. It runs for me instantaneously:
Sub Macro87()
Set Rng = Range("A1:D10000")
For Each cel In Rng
If InStr(1, cel.Value, "Release Date:\n ") > 0 Then
Cells(cel.Row(), 6) = cel.Value
cel.Value = Empty
End If
Next cel
End Sub

Related

VBA Excel - ".Find" Search from top to bottom in order for column K

My goal is to search for a specific value in column "K" and to return a specific result. But an obstacle I'm facing is that it is not going in order from top to bottom when searching the column but rather just executing "find" for "add" even though there is "term" before it. Is there a way to make it read in order cell by cell for the column?
Sub Find_Stuff()
Dim s As String
Dim rCell As Range
Dim lReply As Long
Dim firstaddress As String
Dim rngOriginal As Range
Dim Cell As Range
Dim n As Long
Set Cell = Columns("K:K").Find(What:="Add", LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not Cell Is Nothing Then
firstaddress = Cell.Address
Cell.Offset(0, -9).Resize(, 4).Insert shift:=xlDown
Cell.Offset(0, 1).Value = "add "
n = Range("K" & Rows.Count).End(xlUp).Row
Range("K9").AutoFill Destination:=Range("K9:K" & n), Type:=xlFillDefault
Cell.Select
Else
Set Cell = Columns("K:K").Find(What:="Term", LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not Cell Is Nothing Then
firstaddress = Cell.Address
Cell.Offset(0, -4).Resize(, 4).Insert shift:=xlDown
Cell.Offset(0, 1).Value = "term "
n = Range("K" & Rows.Count).End(xlUp).Row
Range("K9").AutoFill Destination:=Range("K9:K" & n), Type:=xlFillDefault
Cell.Select
Else
Set Cell = Columns("K:K").Find(What:="Remove", LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not Cell Is Nothing Then
firstaddress = Cell.Address
Cell.Offset(0, -4).Resize(, 4).Insert shift:=xlDown
Cell.Offset(0, 1).Value = "Remove"
n = Range("K" & Rows.Count).End(xlUp).Row
Range("K9").AutoFill Destination:=Range("K9:K" & n), Type:=xlFillDefault
Cell.Select
Else
Columns("K:K").Select
Set Cell = Selection.Find(What:="New", After:=ActiveCell, LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not Cell Is Nothing Then
Cell.Offset(0, 1).Select
ActiveCell.FormulaR1C1 = _
"New"
Cell.Select
End If
On Error GoTo 0
End If
On Error GoTo 0
End If
On Error GoTo 0
End If
On Error GoTo 0
End Sub
But an obstacle I'm facing is that it is not going in order from top to bottom when searching the column but rather just executing "find" for "add" even though there is "term" before it. Is there a way to make it read in order cell by cell for the column?
This is the standard behaviour of .Find(). This is the description of the After parameter in MSDN:
The cell after which you want the search to begin. This corresponds to the position of the active cell when a search is done from the user interface. Notice that After must be a single cell in the range. Remember that the search begins after this cell; the specified cell isn't searched until the method wraps back around to this cell. If you do no specify this argument, the search starts after the cell in the upper-left corner of the range.
To make sure that the .Find() starts searching from the first cell, you have to pass as a parameter the last cell:
Sub TestMe()
Dim myR As Range
Dim myS As Range: Set myS = Range("B1:B5")
With myS
Set myR = .Find(1)
Debug.Print myR.Row
Set myR = .Find(1, after:=.Cells(.Cells.Count))
Debug.Print myR.Row
End With
End Sub

How to select a date with a set cell color in Excel?

I am trying to create a macro in excel VBA, that searches the Range (B1:B30) of the value of the ActiveCell in Column “B” by a loop. Along with the search of Column, I also want to check if the date’s cell is colored with a particular color. If the date's cell equals the set color "Good", then I want it to change the color of the cell in Column H of the same row as selected to red.
When I run the code, I get an error message of “Run-time error ‘424’: Object required.” When I go to debug the problem, it highlights the .Find function I have and points to the last line of the search which is “SearchFormat:=False).Activate” What should I do to fix this problem?
Any improvement with my overall code will be very much appreciated.
Sub Find()
Dim FirstAddress As String
Dim MySearch As Variant
Dim Rng As Range
Dim I As Long
MySearch = Array(ActiveCell)
With Sheets("Sheet1").Range("B1:B30")
For I = LBound(MySearch) To UBound(MySearch)
Set Rng = .Find(What:=MySearch(I), _
After:=ActiveCell, _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
SearchFormat:=False).Activate
If Not Rng Is Nothing Then
FirstAddress = Rng.Address
Do
If ActiveCell.Style.Name = "Good" Then
Rng("H" & ActiveCell.Row).Select
Rng.Interior.ColorIndex = xlColorIndexRed
End If
Set Rng = .FindNext(Rng)
Loop While Not Rng Is Nothing And Rng.Address <> FirstAddress
End If
Next I
End With
End Sub
Showing the Debug mode of the run-time error.
Screenshot of the Spreadsheet for reference
Code Review:
You have several problems here.
MySearch = Array(ActiveCell) will always be a single value. So why bother looping through it
You cannot set a range to equal range.activate. Searching Sheets("Sheet1").Range("B1:B30") implies that you are searching a worksheet other that the ActiveSheet. If this is the case than .Find(After:=Activecell) suggests that you are looking for a value after the ActiveCell of another worksheet.
Set Rng = .Find(What:=MySearch(I), _
After:=ActiveCell, _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
SearchFormat:=False).Activate
Rng("H" & ActiveCell.Row) Rng is a Range object. It doesn't work like Range. You cannot pass it a cell address. You can do this Rng(1,"H") which is really shorthand for Rng.cells(1,"H") bit that is misleading because Rng is in column 2 Rng(1,"H") will reference the value in column I.
Sub Find()
Dim FirstAddress As String
Dim MySearch As Variant
Dim Rng As Range
Dim I As Long
MySearch = ActiveCell 'This is the ActiveCell of the ActiveSheet not necessarily Sheets("Sheet1")
With Sheets("Sheet1").Range("B1:B30")
Set Rng = .Find(What:=MySearch, _
After:=.Range("B1"), _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious, _
SearchFormat:=False)
If Not Rng Is Nothing Then
FirstAddress = Rng.Address
Do
If Rng.Style.Name = "Good" Then
.Range("H" & Rng.Row).Interior.ColorIndex = xlColorIndexRed
End If
Set Rng = .FindNext(Rng)
Loop While Not Rng Is Nothing And Rng.Address <> FirstAddress
End If
End With
End Sub
UPDATE:
Here is the actual answer to your question:
Sub FindMatchingValue()
Const AllUsedCellsColumnB = False
Dim rFound As Range, SearchRange As Range
If AllUsedCellsColumnB Then
Set SearchRange = Range("B1", Range("B" & Rows.count).End(xlUp))
Else
Set SearchRange = Range("B1:B30")
End If
If Intersect(SearchRange, ActiveCell) Is Nothing Then
SearchRange.Select
MsgBox "You must select a cell in the highlighted area before continuing", vbInformation, "Action Cancelled"
Exit Sub
End If
Set rFound = SearchRange.Find(What:=ActiveCell.Value, _
After:=ActiveCell, _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
SearchFormat:=False)
If Not rFound Is Nothing Then
Do
If rFound.Style.Name = "Good" Then
Range("H" & rFound.Row).Interior.Color = vbRed
End If
Set rFound = SearchRange.FindNext(rFound)
Loop While Not rFound Is Nothing And rFound.Address <> ActiveCell.Address
End If
End Sub
You can't put Activate at the end of the findthe way you are trying to do.
Try this as you find statement.
Set Rng = .Find(What:=MySearch(I), _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
Rng.Activate
Then if you want to Activate the range, do that. But, it is best to stay away from Select, Activate etc in VBA code. I strongly suggest not using that last line of code and adjust you code to not rely on Select and Activate.
you may want to consider an Autofilter approach so as to loop only through relevant cells, as follows:
Option Explicit
Sub Find()
Dim cell As Range
With Sheets("Sheet1").Range("B1:B30")
.Rows(1).Insert '<--| insert a dummy header cell to exploit Autofilter. it'll be removed by the end
With .Offset(-1).Resize(.Rows.Count + 1) '<--| consider the range expanded up to the dummy header cell
.Rows(1) = "header" '<--| give the dummy header cell a dummy name
.AutoFilter field:=1, Criteria1:=ActiveCell '<--| filter range on the wanted criteria
If Application.WorksheetFunction.Subtotal(103, .Cells) > 1 Then '<--| if any cell other than "header" one has been filtered...
For Each cell In .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible) '<--| ... loop through filtered cells only
If cell.Style.Name = "Good" Then cell.Offset(, 6).Interior.ColorIndex = 3 '<--| ... and color only properly styled cells
Next cell
End If
.AutoFilter '<--| .. show all rows back...
End With
.Offset(-1).Resize(1).Delete '<--|delete dummy header cell
End With
End Sub

Range.Find type mismatch error

Here's my code:
Need my code to enter TRUE in a cell if it finds a value in column A of sheet "Itemschedule" to be present in column B of sheet "Whereused". Getting "Type mismatch" error. If I change the "st = Sheets(..." line to .Value instead of .Text, or if I change the .Find line to LookIn:=xlFormulas instead of Lookin:=xlValues, it gives the same error irrespective of the combination of the two.
Private Sub CommandButton1_Click()
Dim rowLast As Integer
Dim str As String
Dim cell As Range
Sheets("Itemschedule").ListObjects("Table2").Range.AutoFilter
rowLast = Sheets("Itemschedule").Cells(Rows.Count, "A").End(xlUp).Row
Sheets("Itemschedule").ListObjects("Table2").Resize Range("A1:E" & rowLast)
Sheets("Itemschedule").Range("A" & rowLast + 1 & ":E" & Rows.Count).ClearContents
For i = 2 To rowLast
str = Sheets("Itemschedule").Cells(i, "A").Text
With Sheets("Whereused").Range("B:B")
Set cell = .Find(What:=str, After:=.Range("B1"), LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
End With
If cell Is Nothing Then
Sheets("Itemschedule").Cells(i, "E").Value = "FALSE"
Else
Sheets("Itemschedule").Cells(i, "E").Value = "TRUE"
End If
Next
On Error Resume Next
Sheets("Itemschedule").ListObjects("Table2").Range.AutoFilter
Sheets("Itemschedule").Range("A1:E" & rowLast).AutoFilter Field:=1, Criteria1:="FALSE"
Sheets("Itemschedule").Range("A1:E" & rowLast).SpecialCells(xlCellTypeVisible).EntireRow.Delete
Sheets("Itemschedule").ListObjects("Table2").Resize Range("A1:E" & rowLast)
Sheets("Itemschedule").ListObjects("Table2").Range.AutoFilter
End Sub
I have tried a lot but am not able to figure it out. I'm sure it's something silly.
Please help.
Your error is due to the fact that the After parameter is not inside the range you are searching. This part:
With Sheets("Whereused").Range("B:B")
Set cell = .Find(What:=str, After:=.Range("B1"), LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
End With
should be:
With Sheets("Whereused").Range("B:B")
Set cell = .Find(What:=str, After:=Sheets("Whereused").Range("B1"), LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
End With
otherwise the .Range("B1") is relative to Range("B:B") and therefore refers to C1.
Dictionaries are more efficient at finding duplicate values.
Sub CommandButton1_Click()
Dim keyword As String, keyvalue As Variant
Dim rowLast As Long, x As Long
Dim dicItems
Set dicItems = CreateObject("scripting.dictionary")
With Sheets("Whereused")
rowLast = .Cells(Rows.Count, "A").End(xlUp).Row
For x = 2 To rowLast
keyword = .Cells(x, 2)
keyvalue = .Cells(x, 2)
'Add Key Value pairs to Dictionary
If Not dicItems.Exists(keyword) Then dicItems.Add keyword, keyvalue
Next
End With
With Sheets("Itemschedule")
rowLast = .Cells(Rows.Count, "A").End(xlUp).Row
For x = 2 To rowLast
keyword = .Cells(x, 1)
.Cells(x, 2) = dicItems.Exists(keyword)
Next
End With
End Sub
But the proper way to do it place a WorkSheet Formula in Itemschedule Column B.
=COUNTIF(Table1[[#This Row],[Items]],Table2[[#Headers],[Items]])>0

How do I search for a word in Excel Using VBA and then Delete the entire row? [duplicate]

This question already has answers here:
Delete entire row if cell contains the string X
(7 answers)
Closed 9 years ago.
Someone please help.
I'm trying to write a VBA code that searches for a particular word "DR" in my excel worksheet column "D" and then delete the entire row.
There are lots of occurrences of the particular word in the worksheet. All I want to do is to search for the these occurrences and then delete the entire rows that contains those words.
My problem is that I'm not sure what loop structure to use.
Below is the code I'm using.
Columns("D:D").Select
Cells.Find(What:="DR", After:=ActiveCell, LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
, SearchFormat:=False).Activate
Do
Cells.Find(What:="DR", After:=ActiveCell, LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
, SearchFormat:=False).Activate
ActiveCell.EntireRow.Delete
Loop While (Cells.Find(What:="DR"))
I'll be glad for an assistance.
Another Way (the fastest way)
Let's say your worksheet looks like this
You can use the Excel to do the dirty work ;) Use .AutoFilter
See this code
Sub Sample()
Dim ws As Worksheet
Dim lRow As Long
Dim strSearch As String
'~~> Set this to the relevant worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
'~~> Search Text
strSearch = "DR"
With ws
'~~> Remove any filters
.AutoFilterMode = False
lRow = .Range("D" & .Rows.Count).End(xlUp).Row
With .Range("D1:D" & lRow)
.AutoFilter Field:=1, Criteria1:="=*" & strSearch & "*"
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
'~~> Remove any filters
.AutoFilterMode = False
End With
End Sub
Output:
Clean and simple, does the trick! ;)
LastRow = Cells(Rows.Count, "D").End(xlUp).Row
For i = LastRow To 1 Step -1
If Range("D" & i).Value = "DR" Then
Range("D" & i).EntireRow.Delete
End If
Next i
Also another method using Find...
Sub TestDeleteRows()
Dim rFind As Range
Dim rDelete As Range
Dim strSearch As String
Dim sFirstAddress As String
strSearch = "DR"
Set rDelete = Nothing
Application.ScreenUpdating = False
With Sheet1.Columns("D:D")
Set rFind = .Find(strSearch, LookIn:=xlValues, LookAt:=xlPart, SearchDirection:=xlNext, MatchCase:=False)
If Not rFind Is Nothing Then
sFirstAddress = rFind.Address
Do
If rDelete Is Nothing Then
Set rDelete = rFind
Else
Set rDelete = Application.Union(rDelete, rFind)
End If
Set rFind = .FindNext(rFind)
Loop While Not rFind Is Nothing And rFind.Address <> sFirstAddress
rDelete.EntireRow.Delete
End If
End With
Application.ScreenUpdating = True
End Sub
The below example is similar but it starts at the bottom and works its way to the top in reverse order. It deletes each row at a time instead of all at once.
Sub TestDeleteRows()
Dim rFind As Range
Dim rDelete As Range
Dim strSearch As String
strSearch = "DR"
Set rDelete = Nothing
Application.ScreenUpdating = False
With Sheet1.Columns("D:D")
Set rFind = .Find(strSearch, LookIn:=xlValues, LookAt:=xlPart, SearchDirection:=xlPrevious, MatchCase:=False)
If Not rFind Is Nothing Then
Do
Set rDelete = rFind
Set rFind = .FindPrevious(rFind)
If rFind.Address = rDelete.Address Then Set rFind = Nothing
rDelete.EntireRow.Delete
Loop While Not rFind Is Nothing
End If
End With
Application.ScreenUpdating = True
End Sub

Replace data in an Excel file using macros

I have an Excel file which contains some data in a 2d array.
What I want to do is to create a macro which can replace the asterisk '*' by the header of the column of the table (toto, or tata, or titi).
Like this?
Option Explicit
Sub Sample()
Dim oRange As Range, aCell As Range, bCell As Range
Dim ws As Worksheet
Dim ExitLoop As Boolean
On Error GoTo Whoa
'~~> Change this to the relevant sheet name
Set ws = Worksheets("Sheet1")
Set oRange = ws.Cells
Set aCell = oRange.Find(What:="~*", LookIn:=xlValues, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not aCell Is Nothing Then
Set bCell = aCell
'~~> Assuming that the headers are in row 2
aCell.Value = Cells(2, aCell.Column)
Do While ExitLoop = False
Set aCell = oRange.FindNext(After:=aCell)
If Not aCell Is Nothing Then
If aCell.Address = bCell.Address Then Exit Do
'~~> Assuming that the headers are in row 2
aCell.Value = Cells(2, aCell.Column)
Else
ExitLoop = True
End If
Loop
End If
Exit Sub
Whoa:
MsgBox Err.Description
End Sub
Using just worksheet tools (no VBA):
Ctrl-F
Find what = ~*
Find All
Ctrl-A to select all the Find results
Close the Find dialog
Assuming your headers in row two, and assuming the cursor lands in column C somewhere (mine did twice, YMMV), type
formula =C$2
Press Ctrl-Enter
Here is a simple way I came up with.
i = 3
While Cells(2, i).Value <> ""
Range(Cells(3, i), Cells(6, i)).Select
Selection.Replace What:="~*", Replacement:=Cells(2, i).Value, LookAt:=xlPart, _
SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, _
ReplaceFormat:=False
i = i + 1
Wend
Cells(x,y): x refers to row, y refers to column.
A more refined range select can be used instead of this basic one to have the code choose the appropriate range.
To implement in excel simply open up the code window and paste this code in the desired macro/subroutine.