I am currently trying to find a certain word within an excel spreadsheet, copy the cell on the right and then paste it a further 3 cells to the right and 3 cells down, before dragging this down.
I have compiled the following which does the job.
Cells.Find(What:="N/C:", After:=ActiveCell, LookIn:=xlFormulas, LookAt _
:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).Activate
Selection.Offset(0, 1).Select
Selection.Copy
Selection.Offset(3, 3).Select
ActiveSheet.Paste
Range(Selection, Selection.End(xlDown)).Select
Selection.FillDown
My question is:
How can I extend this code so it searches for all "N/C:" and does the above
Feel free to provide updates to my initial code if it can be improved
Lightly tested:
Sub Tester()
Dim col As Collection, c, sht As Worksheet
Set sht = ActiveSheet
Set col = FindAll(sht.UsedRange, "N/C:")
Debug.Print "Found " & col.Count & " matches"
For Each c In col
c.Copy c.Offset(3, 3)
sht.Range(c.Offset(3, 3), c.Offset(3, 3).End(xlDown)).FillDown
Next c
End Sub
Public Function FindAll(rng As Range, val As String) As Collection
Dim rv As New Collection, f As Range
Dim addr As String
Set f = rng.Find(what:=val, 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
The short answer is that there is no built-in way to return a range with all the find results (i.e. find all) in one go. You have to find the first result (the code you already have) and then use findNext within a while loop, exiting only when the next result until it refers to the same cell as the first result.
There is a good explanation/implementation at http://www.cpearson.com/excel/findall.aspx
Related
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
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
I´m writing a simple macro for searching my value in table. I know that searched value is in the document many times. But my macro finds just first value in table. I want select all rows with the value I´m looking for. Then I want copy selected rows and copy them to "sheet2". Can somebody help me adjust my macro? Thx
Sub Vyhladat()
Sheets("Sheet1").Columns(24).Find(What:=InputBox("Please enter your LR number", "Search")).Select
ActiveCells.EntireRow.Select
Selection.Copy
Sheets("Sheet2").Select
Range("A2").Select
Do
If IsEmpty(ActiveCell.Value) Then
ActiveCell.PasteSpecial xlPasteValues
End
Else
ActiveCell.Offset(1, 0).Select
End If
Loop
End Sub
Here is how to do it (find the first match and then loop with the FindNext() method) :
Sub test_Jean()
Dim FirstAddress As String, _
cF As Range, _
RowsToCopy As String
ActiveSheet.Cells(1, 24).Activate
With ActiveSheet.Columns(24)
'First, define properly the Find method
Set cF = .Find(What:=InputBox("Please enter your LR number", "Search"), _
After:=ActiveCell, _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
'If there is a result, keep looking with FindNext method
If Not cF Is Nothing Then
FirstAddress = cF.Address
Do
cF.EntireRow.Copy
Sheets("Sheet2").Range("A" & Sheets("Sheet2").Range("A" & Rows.Count).End(xlUp).Row + 1).PasteSpecial xlPasteValues
Set cF = .FindNext(cF)
'Look until you find again the first result
Loop While Not cF Is Nothing And cF.Address <> FirstAddress
End If
End With
End Sub
i'm having troubles with a Macro that i can't figure it out how to do it, i need a Macro that can scan trough a workbook, Find 3 values, "Data: ", "N°" and "Rodovia:", make a offset of 1 column from them and select that value, and paste it in another sheet like this:
Data: | 10/03/2014
N°: | L02.020.22C
Rodovida: | GO-020
So it must select "10/03/2014", "L02.020.22C" and "GO-020"
I can do it using find and Resize i know, but they are not in the same row or column, they are located in random rows and columns, thats the problem, i tried to use a Range().Select with multiples .Find().offset() inside but it didn't worked
After that i need it to do it with all the cases in the workbook, so i need it to give me that
Case1Data|Case1N°|Case1Rodovia
Case2Data|Case2N°|Case2Rodovia
Case3Data|Case3N°|Case3Rodovia
UPDATE, Code so far:
Sub Gather_Values()
Dim Rng As Range
With Sheets("01").Range("A:AJ")
Set Rng = .Find(What:="Data: ", _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False).Offset(0, 1)
Selection.Copy
Sheets.Add.Name = "New"
Worksheets("New").Activate
Range("A1").Select
Selection.PasteSpecial Paste:=xlPasteValues, _
Operation:=xlNone, SkipBlanks:=False, Transpose:=False
Application.CutCopyMode = False
End With
End Sub
This subroutine will collate the 3 values for a single sheet named "01", searching the UsedRange of that sheet:
Sub Gather_Values()
Dim Rng As Range
Dim Sht As Worksheet
'create new worksheet, name it "New"
Set Sht = Sheets.Add
Sht.Name = "New"
'set column titles in the new sheet
Sht.Range("B1").Value = "Data"
Sht.Range("C1").Value = "N°"
Sht.Range("D1").Value = "Rodovia"
'search the entire UsedRange of sheet 01
With Sheets("01").UsedRange
Set Rng = .Find(What:="Data: ", _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False).Offset(0, 1)
Sht.Range("B2").Value = Rng.Value 'put value from the Find into B column of new sheet
Set Rng = .Find(What:="N°", _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False).Offset(0, 1)
Sht.Range("C2").Value = Rng.Value 'put value from the Find into C column of new sheet
Set Rng = .Find(What:="Rodovia:", _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False).Offset(0, 1)
Sht.Range("D2").Value = Rng.Value 'put value from the Find into D column of new sheet
End With
End Sub
If the values appear more than once on a single sheet you will need to determine either what ranges of cells will always contain separate records or come up with a different way of parsing the data to make sure the values you find in sequence are of the same "case" or "record".
It would be helpful to see a sampling of the raw data that you are parsing to see if there is a better way to collate it than using Find(). It would be best to see an instance where there is more than one "case" in the raw data.
I am completely new for VBA.
I have excel data sheet containing numbers and strings. I want to search for certain string say 'CYP' in column I then look for a cell of its row at column C and copy entire rows containing the string of cell C. I want to paste in sheet 2 of the same workbook and loop it again to look for remaining CYPs in column.
Would you help me on this please?
After the suggestion from pnuts, here is my macro code
Sub Macro1()
'
' Macro1 Macro
'
'
Columns("I:I").Select
Range("I729").Activate
Selection.Find(What:="cyp", After:=ActiveCell, LookIn:=xlValues, LookAt _
:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).Activate
ActiveWindow.SmallScroll Down:=5
Range("C749").Select
Selection.Copy
Columns("C:C").Select
Range("C734").Activate
Selection.Find(What:="EPT001TT0601C000151", After:=ActiveCell, LookIn:= _
xlValues, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext _
, MatchCase:=False, SearchFormat:=False).Activate
Rows("746:750").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Sheet2").Select
ActiveSheet.Paste
End Sub
In this code the CYP was found in I749, cell C749 was copied as string and first row in column C containing the same string was searched followed by copying of the entire row and 4 more followed by it then pasting in sheet2 of the same workbook.
What I wanted was to loop this action again and again upto the end of column I and repeat the same action.
Thank you!
I managed to solve the problem with the help of Trebor76 at Excelforum. Here I am giving solution in that way it might be helpful for some newbies like myself with similar problem.
Option Explicit
Sub Macro1()
'Written and assisted by Trebor76
'Copy an entire row from Sheet1 to Sheet2 for each unique matching item in Col. C if the text in Col. I contains the text 'CYP' (case sensitive)
'http://www.excelforum.com/excel-programming-vba-macros/962511-vba-for-searching-string-in-a-column-and-copy-rows-depending-on-string-in-adjacent-cell.html
Dim rngCell As Range
Dim objMyUniqueArray As Object
Dim lngMyArrayCounter As Long
Dim lngMyRow As Long
Dim varMyItem As Variant
Application.ScreenUpdating = False
Set objMyUniqueArray = CreateObject("Scripting.Dictionary")
For Each rngCell In Sheets("Sheet1").Range("I1:I" & Sheets("Sheet1").Range("I" & Rows.Count).End(xlUp).Row)
If InStr(rngCell, "CYP") > 0 Then
If Not objMyUniqueArray.Exists(Trim(Cells(rngCell.Row, "C"))) Then
lngMyArrayCounter = lngMyArrayCounter + 1
objMyUniqueArray.Add (Trim(Cells(rngCell.Row, "C"))), lngMyArrayCounter
varMyItem = Sheets("Sheet1").Cells(rngCell.Row, "C")
For lngMyRow = 1 To Sheets("Sheet1").Cells(Rows.Count, "C").End(xlUp).Row
If Sheets("Sheet1").Cells(lngMyRow, "C") = varMyItem Then
Rows(lngMyRow).Copy Destination:=Sheets("Sheet2").Range("A" & Sheets("Sheet2").Range("A" & Rows.Count).End(xlUp).Row + 1)
End If
Next lngMyRow
End If
End If
Next rngCell
Set objMyUniqueArray = Nothing
Application.ScreenUpdating = True
MsgBox "All applicable rows have been copied.", vbInformation
End Sub
Cheers!