I am still quite new to VBA and decided I would try and teach myself the Range.FindNext method. Unfortunately, I am not very successful so far.
What I am trying to do, is to copy all rows with a specific search term in them to a new sheet (could be anything, therefore declared as a Variant). What is important, is that the search term might only be part of the cell's value, hence I am using xlPart in my Range.Find method.
Here the example data from my ActiveWorkbook.ActiveSheet:
Date Name Numbers
12.04.2012 Marla 45653
13.04.2017 Peter 23545
27.04.1985 Bertrud 46932
16.08.2020 Peterson 46764
15.09.2014 Marcos 32465
21.06.2010 Peter Pan 23452
31.08.2013 Bernard 12321
So, when looking for "Peter", I should be getting rows 3, 5 and 7 in a new sheet. This is the code I wrote for this:
Option Explicit
Dim wsMain, wsNew As Worksheet
Dim rgAll, rgSearchTermFind As Range
Dim varSearchTerm As Variant
Dim lngLastRow, lngLastColumn As Long
Dim firstAddress As String
Public Sub FindAndCopy()
'I have an InputBox for the user to determine the varSearchTerm, but for this example:
varSearchTerm = "Peter"
Set wsMain = ActiveWorkbook.ActiveSheet
Set wsNew = Sheets.Add(After:=Worksheets(Sheets.Count))
Call FindLast(wsMain) 'This is a separate sub I wrote to find the last row & column
With wsMain
Set rgAll = .Range(.Cells(1, 1), .Cells(lngLastRow, lngLastColumn))
End With
With rgAll
Set rgSearchTermFind = .Find(What:=varSearchTerm, _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlNext, _
MatchCase:=False)
If Not rgSearchTermFind Is Nothing Then
firstAddress = rgSearchTermFind.Address
Do
'Copy row to new sheet
If Application.WorksheetFunction.CountA(wsNew.Cells) <> 0 Then
Call FindLast(wsNew) 'This is a separate sub I wrote to find the last row & column
wsMain.Range(rgSearchTermFind.Address).EntireRow.Copy _
Destination:=wsNew.Cells(lngLastRow + 1, 1)
Else
wsMain.Range(rgSearchTermFind.Address).EntireRow.Copy _
Destination:=wsNew.Cells(1, 1)
End If
'Find next occurrence of search term
Set rgSearchTermFind = .FindNext(rgSearchTermFind)
Loop Until rgSearchTermFind.Address = firstAddress
Else
'Code here to execute if search term could not be found
End If
End With
End Sub
When running this code, the initial Range.Find method finds Peter in B3, but the Range.FindNext then finds "Bertrud" in B4 and copies it. This happens for each cell in the range, leaving me at the end with the table copied three times in the new sheet (due to there being three columns).
What am I doing wrong? Any help will be much appreciated.
Related
MY EXCEL FILE
Two worksheets: Report and Leaving
One Named Range: Leavers (list of full names in the Leaving worksheet, column A, some names are red and others are orange).
MY GOAL
I want my macro to find in the Report worksheet (specifically in columns G to M) all the names that are part of the Leavers named range and apply the matching font color to each cell that was found.
MY CODE (SO FAR...)
This code could help by searching them one by one but it doesn't change much from doing it manually with Ctrl + F one by one. I could not find another way around it. Feel free to offer better alternatives, codes and solutions.
Dim Sh As Worksheet
Dim Found As Range
Dim Nme As String
Dim Adr1 As String
Nme = Application.InputBox("Enter Name to search", "Test")
Set Sh = Sheets("Sheet1")
With Sh.Range("A2:A")
Set Found = .Find(What:=Nme, After:=.Range("A2"), _
LookIn:=xlValues, lookat:=xlWhole, SearchOrder:=xlNext, _
MatchCase:=False, SearchFormat:=False)
If Not Found Is Nothing Then
Adr1 = Found.Address
Else
MsgBox "Name could not be found"
Exit Sub
End If
Do
Found.Interior.ColorIndex = 4
Set Found = .FindNext(Found)
Loop Until Found Is Nothing Or Found.Address = Adr1
End With
End Sub
Try this:
I tried to stick with some of your existing code but I had to make some changes.
You need to loop through your first range (I've used "G2:M1000" here on Sheet1 which I guess is your report page?)
You can't use a range like "A2:A" in your find routine, so again I've arbitrarily used a 1000 limit: "A2:A1000"
You were using interior cell colour, not font colour, I've changed this but if you did mean interior colour then just swap it back
I'm not using "Exit Sub" since this will stop everything running the first time it encounters a blank cell / no matching name.
Sub eh()
Dim rng As Range: Set rng = Sheet1.Range("G2:M1000")
Dim v As Range
Dim c As Variant
Dim Found As Range
Dim Nme As String
Dim Adr1 As String
For Each v In rng
Nme = v.Value2
If Nme <> "" Then
Set Found = Sheet2.Range("A2:A1000").Find(What:=Nme, After:=Sheet2.Range("A2"), LookIn:=xlValues, lookat:=xlWhole, SearchOrder:=xlNext, MatchCase:=False, SearchFormat:=False)
If Not Found Is Nothing Then
v.Font.Color = Found.Font.Color
End If
End If
Next v
End Sub
I tried a different approach, I have a subprocedure to loop through the range of the leavers, and when I find a value i give it to another procedure to look for that value in the report range. I am copying all the format of the cells, you can change it to just copy the font color. Also you should check for the ends of each range, to optimize and also this code would loop several times for the same name if the same name repeats in the leavers range, that could be improved.
Sub select_name() 'Select every cell in the leavers range
Dim leaving_range As Range
Dim row_leaving As Range
Set leaving_range = Sheets("Leaving").Range("A2:A10") 'The leavers list, check for the end of the range
For Each row_leaving In leaving_range.Rows
If row_leaving.Cells(1, 1).Text <> "" Then
row_leaving.Cells(1, 1).Copy 'I am gonna copy all the format, you change it to copy just the font color
Call look_for_name(row_leaving.Cells(1, 1).Text)
End If
Next
End Sub
Sub look_for_name(name_to_find As String) 'look for a name in the report range and change the format
Dim report_range As Range
Dim row_report As Range
Set report_range = Sheets("Report").Range("G1:M5") 'the report range where the names are to be found
For Each row_report In report_range.Rows
For Each cell In row_report.Cells
If cell.Value = name_to_find Then
cell.PasteSpecial Paste:=xlPasteFormats
End If
Next
Next
End Sub
I am trying to connect to workbooks to track orders. One workbook(Book1) displays the order#, total qty and current qty(completed so far) alongside a list of the weeks orders in the production office. The other workbook(Book2) is at the workstation for the operator to enter the new order number and current quantity as parts are completed.
The first half of the code works fine. It successfully updates the order# and pastes it to Book1 from Book2. What I am having trouble accomplishing is updating the cell in the "status" column of the table with the corresponding order# that was just pasted into the workbook to either a 1 or 2. I have the table formatted to where a blank cell is red(order not active), 1 = yellow(order is open) and 2 = green(order complete).
I tried the code below using an "IF" off of the Order Count being 0 because it will reset before the pasting of the new Order#. NOTE: orders may not be completed in the order they are listed so it has to be some type of lookup. I can't just find the last empty cell in the "status" column.
Update* FIGURED IT OUT! Code below now works in case anyone elses comes across this thread!
Thank you to everyone in the comments below.
Private Sub CommandButton1_Click()
Dim wbEntry As Workbook
Set wbEntry = ThisWorkbook
Dim wbCount As Workbook
Set wbCount = Workbooks("MO# Count.xlsm")
wbEntry.Sheets("Sheet1").Range("B3").Copy
wbCount.Activate
wbCount.Worksheets("Golf Cart").Range("V5").Select
ActiveCell.PasteSpecial xlPasteValues
Dim Fnd As Range
Set Fnd = Sheets("Golf Cart").Range("A:A").Find(Sheets("Golf Cart").Range("V5").Value, , , xlWhole, , , False, , False)
If Not Fnd Is Nothing Then
Set Fnd = Fnd.Offset(0, 2)
End If
Fnd.Value = 1
ActiveWorkbook.Save
wbEntry.Activate
Application.CutCopyMode = False
wbEntry.ActiveSheet.Range("H2").Select
End Sub
If you write What:="MO" then you are searching literally for the text "MO" and not for the value in the variable MO. To use the variable you must write What:=MO
For every Range object specify in which workbook/worksheet it is or Excel might assume another worksheet than you thought.
Only search in Column A wsGolf.Columns("A").Find(…), if you search in Cells you search in all cells and of course you will always find what you are looking for in Range("V5") but that's not the result you want.
Check if Find was successfull: If FoundRef Is Nothing Then. You can only Offset from the found cell if one was actually found.
Never use .Select, .Activate or ActiveSheet always specify worksheets by their name for all Range and Cells objects etc. See How to avoid using Select in Excel VBA.
So something like below should help you:
Option Explicit
Private Sub CommandButton1_Click()
Dim wbEntry As Workbook
Set wbEntry = ThisWorkbook
Dim wbCount As Workbook
Set wbCount = Workbooks("MO# Count.xlsm")
wbEntry.Worksheets("Sheet1").Range("B3").Copy
Dim wsGolf As Worksheet
Set wsGolf = wbCount.Worksheets("Golf Cart")
wsGolf.Range("V5").PasteSpecial xlPasteValues
Dim MO As Range
Set MO = wsGolf.Range("V5")
Dim FoundRef As Range
Set FoundRef = wsGolf.Columns("A").Find(What:=MO, After:=wsGolf.Range("A1"), LookIn:=xlValues, LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
If FoundRef Is Nothing Then
MsgBox "'" & wsGolf.Range("V5") & "' was not found.", vbCritical
Exit Sub
End If
FoundRef.Offset(ColumnOffset:=2).Value = 1
End Sub
If anyone is willing to help me, I'd be most grateful.
Basically I would like to perform the same task as what was featured in this thread...
Excel Marcro - delete a row based on row number
I have a list of 3500+ lines and need to occasionally delete about 25-30 (non-consecutive) rows based on row number (the row #s will vary every time). I'd like to list the row numbers (to be deleted) on Sheet2 (in Column A) and have the code automatically read the row numbers on Sheet2 and delete those rows from Sheet1. I tried the code provided in the thread and I get a "run-time error 424" Object required. If I mouse over the error it tells me "SourceWks = Nothing" and "Sheet2 = Empty". I do have data contained in the sheet(s). I'm sure I'm just missing something simple.
I'll paste the code I'm using below (again it is from the original thread which was reported by the user to work just fine)...
Dim deleteRows As Range
Dim data() As Variant
Dim i As Double
Dim SourceWks As Worksheet, deleteWks As Worksheet
Set SourceWks = Sheet2
Set deleteWks = Sheet1
With SourceWks
data = .Range(.Cells(1, 1), .Cells(1, 1).End(xlDown))
End With
Set deleteRows = deleteWks.Rows(data(1, 1))
For i = 2 To UBound(data, 1)
Set deleteRows = Union(deleteRows, deleteWks.Rows(data(i, 1)))
Next i
deleteRows.Delete Shift:=xlUp
I've tried it both ways...keeping Sheet1 named "Sheet1" and Sheet2 named "Sheet2" and I've also tried changing the sheets to be named to: "deleteWKS" and "SourceWks" all with the same results.
If anyone can please let me know what I'm doing incorrectly, I'd be most appreciative.
Consider:
Sub rowKiller()
Dim SourceWks As Worksheet, deleteWks As Worksheet
Dim rng As Range, i As Long
Set SourceWks = Sheet2
Set deleteWks = Sheet1
Set rng = SourceWks.Columns(1).SpecialCells(2)
rng.Sort Key1:=rng(1), Order1:=xlDescending, Header:=xlNo, _
OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
DataOption1:=xlSortNormal
For Each r In rng
i = r.Value
deleteWks.Rows(i).EntireRow.Delete
Next r
End Sub
NOTES:
The reason we sort the row list descending because we want to delete the rows in Sheet1 from the bottom upwards.
I am new to both stackoverflow.com and VBA within Excel, so go easy on me :-)
I am looking to have a button in my excel sheet that when clicked, will search the entire or row 1 for a date (located in another cell). If it finds the date in a call in row 1, it will enter some text in the cell below it. If it doesn't find the date, it will add the date to the next free cell in the row and then add the text in the cell below that.
I know I have asked a lot and I am happy to accept partial answers as I know how to do some of the aspect of this. For example the pasting of text and such. The part I am finding difficult is the finding on the date in the entire row 1 and then finding the next blank cell if no date is found.
Any help or pointers will be highly appreciated!
However, I would be even happier if I get a response, the person also explains how the code works as I am very keen to learn VBA and use it again in the future and not just copy and paste.
Thanks in advance for any replies! :-)
Give this a try. I've commented code in details, but if you have some questions, ask in comments:)
Sub test()
Dim ws As Worksheet
Dim rng As Range
Dim targetDate As Range
'change sheet1 to suit
Set ws = ThisWorkbook.Worksheets("Sheet1")
'change address of your cell with target date
Set targetDate = ws.Range("A4")
'tries to find target date in first row
Set rng = ws.Range("1:1").Find(What:=targetDate, LookAt:=xlWhole, MatchCase:=False)
If rng Is Nothing Then
'if nothing found - search for last non empty column
Set rng = ws.Range("1:1").Find(What:="*", LookAt:=xlWhole, MatchCase:=False, SearchOrder:=xlByColumns, SearchDirection:=xlPrevious)
If rng Is Nothing Then
'if row 1 is empty
Set rng = ws.Range("A1")
Else
'take next column after last non empty
Set rng = rng.Offset(, 1)
End If
'write target date
rng = targetDate
rng.NumberFormat = "dd.mm.yyyy"
'write something below the date
rng.Offset(1) = "test"
Else
'if date is found - write something below the date
rng.Offset(1).Value = "test2"
End If
End Sub
I am struggling to find an answer to this one...
Each month I am provided with a spreadsheet full of clients data that is a raw extract from some sort of CRM software and that data is a mess. Some cells are merged, some are not. When you unmerge the whole sheet, you end up with data that is meant for one column randomly spread across 3 columns and mixed with another data, ie email addresses are spread across 3 columns and mixed with postcodes.
What I'd like to be able to do is search for cells within columns S, T and U that contain "#" and move (not copy) the whole email address to column V on the same row.
How can I achieve that?
You can achieve this with the following formula into V1:
=INDEX(S1:U1,MATCH(TRUE,NOT(ISERROR(SEARCH("#",S1:U1))),0))
The formula needs to be entered as array formula, i.e. pressing Ctrl-Shift-Enter.
Press Alt+F11 to open the Visual Basic Editor, and then click Insert, Module. Paste this in. Or, just download the example file here. Then under View/Macros, this movemail() routine will be there. Run it.
I take check, money order, paypal, bitcoin... :-) j/j Enjoy.
Sub moveemail()
Dim ws As Worksheet
Dim thisCell As Range, nextCell As Range, lookAt As Range
Dim foundAt As String, lookFor As String
Dim lastRow As Long
lookFor = "#"
On Error GoTo Err
'get last populated cell
Set ws = Application.ActiveSheet
With ws
If WorksheetFunction.CountA(Cells) > 0 Then
lastRow = Cells.Find(what:="*", SearchOrder:=xlByRows, _
SearchDirection:=xlPrevious).Row
End If
End With
' go cell by cell looking for # from row 1 to row 10000
Set lookAt = ActiveSheet.Range("S1:U" & lastRow)
Set thisCell = lookAt.Find(what:=lookFor, LookIn:=xlValues, lookAt:=xlPart, SearchDirection:=xlNext)
If Not thisCell Is Nothing Then
Set nextCell = thisCell
Do
Set thisCell = lookAt.FindNext(After:=thisCell)
If Not thisCell Is Nothing Then
foundAt = thisCell.Address
thisCell.Copy Range("V" & thisCell.Row)
thisCell.ClearContents
Else
Exit Do
End If
If thisCell.Address = nextCell.Address Then Exit Do
Loop
Else
'MsgBox SearchString & " not Found"
Exit Sub
End If
Err:
'MsgBox Err.Number
Exit Sub
End Sub