Find list of words from a range if words exits multiple times - excel

I have a list of words in Sheet1 I need to match one by one from Sheets("Sheet2").Range("A1:A7500") until the end of the Range. Whenever word is matched I need to do something with it in Sheet1. That word occurs multiple times in Sheets("Sheet2").Range("A1:A7500").
Following code is Finding word only once. I dont understand where it is going wrong.
Sub XMAX()
Dim lrow As Long
Dim cel As Range
Dim oRng As Range: Set oRng = Sheets("Sheet2").Range("A1:A7500")
Dim oFoundRng As Range, oLastRng As Range
lrow = Sheets("sheet1").Cells(Sheets("Sheet1").Rows.Count, "f").End(xlUp).Row
'''''''''''''''Sheet1'''''''''''''''
For Each cel In Range("f4:f" & lrow)
If IsEmpty(cel.Value) = False Then
Set oFoundRng = oRng.find(cel.Value)
Do While Not oFoundRng Is Nothing
If UCase(oFoundRng.Offset(0, 1).Value) = "ISAAC" Then
Range("X" & cel.Row).Value = "X"
ElseIf UCase(oFoundRng.Offset(0, 1).Value) = "YO" Then
Range("V" & cel.Row).Value = "X"
ElseIf UCase(oFoundRng.Offset(0, 1).Value) = "JAN" Then
Range("U" & cel.Row).Value = "X"
Else
MsgBox oFoundRng.Value
End If
Set oLastRng = oFoundRng
Set oFoundRng = oRng.FindNext(cel.Value) 'Getting Error(1004) here "unable to get findnext property of the range class"
If oLastRng >= oFoundRng Then
Exit Do
End If
Loop
Else
End If
Next

Change this line
Set oFoundRng = oRng.FindNext(oFoundRng)
to
Set oFoundRng = oRng.FindNext
You are not searching for the word but for the range you previously found. You actually don't need to pass a value to .FindNext at all.
You also have to change this line
If oLastRng >= oFoundRng Then
to
If oLastRng.Row >= oFoundRng.Row Then
since the first line compares the values (which is not what you want to do since it will always evaluate to True). You actually want to compare the row numbers.
On another note, the following code snippet does not work:
If UCase(oFoundRng.Offset(0, 1).Value) = "ISAAC" Then
Range("X" & cel.Row).Value = "X"
ElseIf UCase(oFoundRng.Offset(0, 1).Value) = "ISAAC" Then
Range("W" & cel.Row).Value = "X"
This ElseIf will never be triggered since the condition is the same as the initial If condition.
You also don't need both of these statements:
Set oFoundRng = Nothing
Exit Do
They both achieve the same thing (breaking the loop), Exit Do does it more efficiently.

you may be after this (explanations in comments):
Sub XMAX()
Dim cel As Range
Dim oRng As Range: Set oRng = Sheets("Sheet2").Range("A1:A7500")
Dim oFoundRng As Range
Dim firstAddress As String
With Sheets("sheet1") ' reference "Sheet1" sheet
With .Range("f4", .Cells(.Rows.Count, "f").End(xlUp)) ' reference referenced sheet column "F" range from row 4 down to last not empty one
If WorksheetFunction.CountA(.Cells) > 0 Then ' if there's at least one not empty cell
For Each cel In .SpecialCells(xlCellTypeConstants) ' loop through referenced range not empty cells
Set oFoundRng = oRng.Find(what:=cel.Value, LookIn:=xlValues, lookat:=xlWhole) ' always specify at least 'LookIn' and 'LookAt' parameters, or they will be set as per last 'Find()' usage (even from Excel UI!)
If Not oFoundRng Is Nothing Then ' if a match found
firstAddress = oFoundRng.Address ' store first matched cell address
Do
Select Case UCase(oFoundRng.Offset(0, 1).Value2)
Case "ISAAC"
.Range("X" & cel.Row).Value = "X"
Case "YO"
.Range("V" & cel.Row).Value = "X"
Case "JAN"
.Range("U" & cel.Row).Value = "X"
Case Else
MsgBox oFoundRng.Value
End Select
Set oFoundRng = oRng.FindNext(oFoundRng) ' search for next occurrence
Loop While oFoundRng.Address <> firstAddress ' exit do when hitting fisr found cell again
End If
Next
End If
End With
End With
End Sub

Related

How do I exit a "do while" statement

Sub FindValue()
Dim firstAddress As String
Dim Expense As String
Dim rRange As Range
Dim FirstrngFnd As Range
Dim x As Integer
Dim y As Integer
Dim z As Integer
Workbooks.Open FileName:= _
"D:\My Documents\Excel Files\AA Credit Card\1008.xlsx" 'A worksheet with several columns - Column G (Column 7) is a list of "expenses".
Workbooks.Open FileName:= _
"D:\My Documents\Excel Files\Credit Card Analysis.xlsx" 'A worksheet with "expenses" listed in random order in Column A (Column1)
Windows("Credit Card Analysis.xlsx").Activate
ActiveWindow.Panes(1).Activate
Expense = Application.InputBox("Select the required expense from Column A") 'Pick an "expense" from a list in Column A
'Open the first file in the first folder
Windows("1008.xlsx").Activate
ActiveWindow.Panes(1).Activate
'Establish the first and last rows in Column A (which contain a list of dates by increasing date): required to establish the search range
Set rRange = Range("A1", Cells(Rows.Count, 1).End(xlUp))
For Each rCell In rRange
If IsDate(rCell) Then
rCell(2, 1).Select
Exit For
End If
Next rCell
x = (ActiveCell.Row - 1) 'This finds the FIRST row in the file with a date in it
y = Cells(Rows.Count, 1).End(xlUp).Row 'This finds the LAST row in the file with a date in it
My_Workbook = ActiveWorkbook.Name 'Holds the current Workbook name
'Move over to the "analysis" column (G) (Column 7)
With Worksheets(1).Range(Cells(x, 7), Cells(y, 7))
Set FirstrngFnd = .Find(Expense, LookIn:=xlValues, LookAt:=xlPart) 'Finds the first occurrence of "expense"
If Not FirstrngFnd Is Nothing Then 'if the "expense" isn't listed then goto Line400
firstAddress = FirstrngFnd.Address
Do 'DO WHATEVER IS REQUIRED IN THIS SECTION: FROM "DO" TO "Set FirstrngFnd = .FindNext(FirstrngFnd)"
z = FirstrngFnd.Row
FirstrngFnd.Value = "Mike" 'IF YOU OMIT THIS LINE THEN ALL THE VALUES REMAIN AT "expense", SO THE PROGRAM JUST GOES ROUND (AND ROUND) AGAIN.
Set FirstrngFnd = .FindNext(FirstrngFnd)
Loop While Not FirstrngFnd Is Nothing
End If
End With
End Sub
If I remove the line "FirstrngFnd.Value = "Mike" then the values in Golumn G never change so when the program gets to the end of the file, it just goes round again.
How can I get it to recognise it's been through the file once, and to move on?
Please, modify:
Loop While Not FirstrngFnd Is Nothing
in this way:
Loop While Not FirstrngFnd Is Nothing And FirstrngFnd.Address <> firstAddress
Find in an Endless Loop
This covers the case when you are using the Find method (incl. FIndNext) to find all occurrences of a value in a range, but you don't want to change them and you also don't want to end up in an endless loop.
To simplify, the procedures use Sheet1 (CodeName) and the range "A1:A3" and "Yes" as the criteria.
Copy them into a new workbook.
Test first both procedures without values, then put in some "Yes" values (without the double quotes) in the cells. You can also change the range to e.g. A1:A10, to better understand.
The Code
Option Explicit
' To exit the endless loop, press and hold down the ESC key.
Sub testEndlessLoop()
Dim rng As Range
Dim cel As Range
Set rng = Sheet1.Range("A1:A3")
Set cel = rng.Find( _
What:="Yes", _
After:=rng(rng.Cells.Count), _
LookIn:=xlFormulas)
' Assuring that a cel containing "Yes" has been found. Once it has
' been found, find will always find an occurrence of "Yes" whether
' there is one, two or three occurrences in the range,
' which will result in an endless loop (Do Loop).
If Not cel Is Nothing Then
' A cell containing "Yes" has been found.
Do
MsgBox "Found ""Yes"" in cell '" & cel.Address & "'."
' Find the next occurrence of "Yes"
Set cel = rng.FindNext(cel)
' 'While Not cel Is Nothing' is redundant because 'cel' will
' never be 'Nothing' anyway, which is ensured previously with
' 'If Not cel Is Nothing Then'.
Loop While Not cel Is Nothing
Else
' A cell containing "Yes" has not been found.
MsgBox "No occurrences found."
End If
End Sub
Sub testWorkingLoop()
Dim rng As Range
Dim cel As Range
Dim FirstAddress As String
Set rng = Sheet1.Range("A1:A3")
Set cel = rng.Find( _
What:="Yes", _
After:=rng(rng.Cells.Count), _
LookIn:=xlFormulas)
If Not cel Is Nothing Then
' A cell containing "Yes" has been found.
' Write the range address of the first found occurrence to a variable.
FirstAddress = cel.Address
Do
' This is where you do stuff when "Yes" is found.
MsgBox "Found ""Yes"" in cell '" & cel.Address & "'."
' Most often you don't want to change the found cell,
' but you want to change another cell in the same row,
' e.g. write to the cell in column 'B'.
cel.Offset(0, 1).Value = "Found a ""Yes""."
' This is where you do stuff when "Yes" is found.
' Find the next occurrence of "Yes"
Set cel = rng.FindNext(cel)
' Again, 'cel' will never be 'Nothing'.
Loop While cel.Address <> FirstAddress
Else
' A cell containing "Yes" has not been found.
MsgBox "No occurrences found."
End If
End Sub

building a loop based on if statement of two ranges in vba

Thank you in advance for your help.
I am trying to build a macro (which in the end will be part of a bigger macro) that will compare two IDs and based on findings will perform another operation.
The code that I have at the moment only copies the values for each row without any consideration of ID in the first column. Here is the code:
Sub movingValues()
'declaring/setting variables
Dim SheetOneWs As Worksheet, SheetTwoWs As Worksheet
Dim SheetOneLastRow As Long, SheetTwoLastRow As Long
Dim SheetOneRng As Range, SheetTwoRng As Range
Dim cell As Range, i As Integer
Application.Calculation = xlCalculationManual
Set SheetOneWs = ThisWorkbook.Worksheets("SheetOne")
Set SheetTwoWs = ThisWorkbook.Worksheets("SheetTwo")
SheetOneLastRow = SheetOneWs.Range("A" & Rows.Count).End(xlUp).Row
SheetTwoLastRow = SheetTwoWs.Range("A" & Rows.Count).End(xlUp).Row
Set SheetOneRng = SheetOneWs.Range("A2:D13" & SheetOneLastRow)
Set SheetTwoRng = SheetTwoWs.Range("A2:M13" & SheetTwoLastRow)
SheetOneWs.Range("B2:D13").Value = ""
For i = 2 To SheetTwoLastRow
'For Each cell In SheetTwoWs.Range(Cells(i, "B"), Cells(i, "M"))
For Each cell In SheetTwoWs.Range("B" & i & ":" & "M" & i)
If cell.Value = "No" Then
SheetOneWs.Cells(cell.Row, "B").Value = SheetTwoWs.Cells(1, cell.Column)
Exit For
End If
SheetOneWs.Cells(cell.Row, "B").Value = "No data"
Next cell
For Each cell In SheetTwoWs.Range("B" & i & ":" & "M" & i)
If cell.Value = "Maybe" Then
SheetOneWs.Cells(cell.Row, "C").Value = SheetTwoWs.Cells(1, cell.Column)
Exit For
End If
SheetOneWs.Cells(cell.Row, "C").Value = "No data"
Next cell
For Each cell In SheetTwoWs.Range("B" & i & ":" & "M" & i)
If cell.Value = "Yes" Then
SheetOneWs.Cells(cell.Row, "D").Value = SheetTwoWs.Cells(1, cell.Column)
Exit For
End If
SheetOneWs.Cells(cell.Row, "D").Value = "No data"
Next cell
Next i
Application.Calculation = xlCalculationManual
End Sub
My understanding is that I need to place that inside of another loop to match the IDs, so far I've tried:
For i = 2 To SheetOneLastRow
For a = 2 To SheetTwoLastRow
valTwo = Worksheets("SheetTwo").Range("A" & a).Value
If Cells(i, 1) = valTwo Then
'CODE FROM ABOVE'
End if
Next a
Next i
doesn't seem to work the way I intend it too, all your help will be greatly appreciated. The code initially was taken from the answer in here: Issue with copying values based on condition from one sheet to another VBA
Thank you once again for all your answers.
Best Regards,
Sergej
As far as I can tell, this does what you want.
Sub x()
Dim rID As Range, rMonth As Range, rData As Range, rCell As Range, v As Variant
With Worksheets("SheetTwo")
Set rID = .Range("A2", .Range("A" & Rows.Count).End(xlUp))
Set rMonth = .Range("B1:M1")
Set rData = .Range("B2").Resize(rID.Rows.Count, rMonth.Columns.Count)
End With
With Worksheets("SheetOne")
For Each rCell In .Range("A2", .Range("A" & Rows.Count).End(xlUp))
v = Application.Match(rCell.Value, rID, 0)
If IsNumeric(v) Then
rCell.Offset(, 1).Value = rMonth.Cells(Application.Match("No", rData.Rows(v), 0))
rCell.Offset(, 2).Value = rMonth.Cells(Application.Match("Maybe", rData.Rows(v), 0))
rCell.Offset(, 3).Value = rMonth.Cells(Application.Match("Yes", rData.Rows(v), 0))
End If
Next rCell
End With
End Sub
Because I couldn't really bear looking at your horribly inefficient code, I've reworked it here based on the data provided in your previous question.
What this does is it loops over sheet 2 column A. Then for every cell it finds the corresponding ID and stores the row in "Hit".
It then finds three values in the row of the cell, and adds the month linked to every hit to the correct place in an array.
Then it pastes the array in one go to the correct range in sheet 1.
Sub movingValues()
Dim SheetOneWs As Worksheet, SheetTwoWs As Worksheet
Dim SheetOneLastRow As Long, SheetTwoLastRow As Long
Dim cel As Range, hit As Range
Dim Foundrow As Integer
Dim arr() As Variant
Application.Calculation = xlCalculationManual
Set SheetOneWs = ThisWorkbook.Worksheets("Sheet1")
Set SheetTwoWs = ThisWorkbook.Worksheets("Sheet2")
SheetOneLastRow = SheetOneWs.Range("A" & Rows.Count).End(xlUp).Row
SheetTwoLastRow = SheetTwoWs.Range("A" & Rows.Count).End(xlUp).Row
ReDim arr(1 To SheetOneLastRow - 1, 1 To 3)
For Each cel In SheetTwoWs.Range("A2:A" & SheetTwoLastRow)
Foundrow = SheetOneWs.Range("A1:A" & SheetOneLastRow).Find(cel.Value).Row - 1
If Not Foundrow = 0 Then
Set hit = SheetTwoWs.Rows(cel.Row).Find("No", SearchDirection:=xlNext)
If Not hit Is Nothing Then
arr(Foundrow, 1) = SheetTwoWs.Cells(1, hit.Column).Value
Else
arr(Foundrow, 1) = "No Data"
End If
Set hit = SheetTwoWs.Rows(cel.Row).Find("Maybe", SearchDirection:=xlNext)
If Not hit Is Nothing Then
arr(Foundrow, 2) = SheetTwoWs.Cells(1, hit.Column).Value
Else
arr(Foundrow, 2) = "No Data"
End If
Set hit = SheetTwoWs.Rows(cel.Row).Find("Yes", SearchDirection:=xlNext)
If Not hit Is Nothing Then
arr(Foundrow, 3) = SheetTwoWs.Cells(1, hit.Column).Value
Else
arr(Foundrow, 3) = "No Data"
End If
End If
Next cel
SheetOneWs.Range("B2:D" & SheetOneLastRow) = arr
End Sub
As you can probably see when trying it, reading your values into an array first makes this pretty much instant, since it saves on "expensive" write actions. With the tests in place and this structure it should be much more straightforward and rigid than your previous code. Using Find means it only needs to loop over each row once, further increasing performance.
Please note, it's best to back up your data before trying in case of unexpected results and/or errors.

find row number of cell that contains criteria

I'm needing to find the first row numbers of cell in column C that contains "120" without duplicates (data I have has more than 10 of each number code, I only need the first one). So the code should pick up the first row number containing e.g. 120, 7120, 81200.
The code I've tried below have only managed to find the first row number with cell that contained 120. For reference, AGCL is a column letter derived from another find function and tbAC is a user input into a textbox.
Dim AGCN As Long
Dim AGCL As String
Dim AGNN As Long
Dim AGNL As String
Dim i As Long
Dim RowD As Long
Dim AAC As String
Dim rng As Range
Dim rownumber As Long
Dim AGC As Range
Dim AGN As Range
Dim firstaddress As Long
Dim nextaddress As Long
Set rng = Sheet1.Columns(AGCL & ":" & AGCL).Find(what:="*" & tbAC & "*",
LookIn:=xlValues, lookat:=xlPart)
rownumber = rng.Row
Debug.Print rownumber '9
With Sheet1.Range(AGCL & ":" & AGCL)
Set c = .Find("*" & tbAC & "*", LookIn:=xlValues)
If Not c Is Nothing Then
firstaddress = c.Value
Debug.Print firstaddress
With Me.ListBox2
.ColumnCount = 3
.ColumnWidths = "50;150;70"
.AddItem
.List(i, 0) = Str(firstaddress)
i = o + 1
End With
Do
Set c = .FindNext(c)
If c Is Nothing Then
GoTo donefinding
ElseIf firstaddress <> c.Value Then
nextaddress = c.Value
Debug.Print nextaddress 'it doesn't print any value here
'With Me.ListBox2
' .ColumnCount = 3
' .ColumnWidths = "50;150;70"
' .AddItem
' .List(i, 0) = Str(nextaddress)
' Debug.Print nextaddress
' i = o + 1
'End With
End If
Loop While c.Address <> firstaddress
End If
donefinding: Exit Sub
End With
Any help would be greatly appreciated, thank you!
Here is the Range.FindNext Function you can use to retrieve all the cells having 120.
With Sheet1.Range(AGCL & ":" & AGCL)
Set c = .Find("*" & tbAC & "*", lookin:=xlValues)
If Not c Is Nothing Then
firstAddress = c.Address
Do
Set c = .FindNext(c)
If c is Nothing Then
GoTo DoneFinding
Elseif not firstaddress.value = c.value
''Whatever you want to do with the Second Found Value
debug.print c.value
End If
Loop While c.Address <> firstAddress
End If
DoneFinding:
End With
Now to check that the value already found or not, you can play in the If Condition of this loop. So that you don't get the same values again.
UPDATED: Okay I updated one last time. As mentioned, I don't know what you want to do with the extra values... but this function will output them where ever...?
good luck.
Here's a custom function that matches what you're looking for, it will return the first time that 120 appears in a cell...
Here's one more that you could use if you truly wanted "contains" only a partial match.
Function SuperSearcherTHING(ivalue As Variant, theColumn As Range) As String
Dim rCell As Range
Const theSPACER As String = "|"
For Each rCell In Intersect(theColumn.EntireColumn, theColumn.Worksheet.UsedRange).Cells
If InStr(1, rCell.Value, ivalue, vbTextCompare) > 0 Then
SuperSearcherTHING = rCell.Value & theSPACER & SuperSearcherTHING
End If
Next rCell
SuperSearcherTHING = Left(SuperSearcherTHING, Len(SuperSearcherTHING) - Len(theSPACER))
End Function

Hiding row if cell equals next visible cell

I am trying to write a macro that hides the row if the cell value equals the next visible cell in that column and loops through the whole column. I have read that SpecialCells(xlCellTypeVisible) only works up to 8192 cells and my spreadsheet has 15,000 rows.
I have tried something like this but want to restrict it to only visible cells
Sub Test()
For i = 7 To 15258
If Range("P" & i).Value = Range("P" & i + 1).Value Then
Rows(i).Hidden = True
End If
Next i
End Sub
I have tried to search for a solution but haven't been able to find one yet.
Thanks!
I'd be surprised if this couldn't be optimized just a little bit, but it will work for what you are needing.
You can follow the comments within the code itself to kind of get a sense of what it's doing, but in a nutshell, you are using a For...Next statement to loop through your visible cells. For each visible cell, you will search for the next visible cell and then check to see if that matches. If it does, you add that cell to a special range that tracks all the rows to hide at the end of the code, then hide it.
Sub Test()
Dim ws As Worksheet, lookupRng As Range, rng As Range, lstRow As Long
Set ws = ThisWorkbook.Worksheets(1)
lstRow = 15258
Set lookupRng = ws.Range("P7:P" & lstRow)
Dim rngToHide As Range, i As Long
For Each rng In lookupRng.SpecialCells(xlCellTypeVisible)
Application.StatusBar = "Checking row " & rng.Row & " for matches."
For i = rng.Row + 1 To lstRow 'Loop through rows after rng
If Not ws.Rows(i).Hidden Then 'Check if row is hidden
If rng.Value = ws.Cells(i, "P") Then 'check if the non-hidden row matches
If rngToHide Is Nothing Then 'Add to special range to hide cells
Set rngToHide = ws.Cells(i, "P")
Else
Set rngToHide = Union(rngToHide, ws.Cells(i, "P"))
End If
End If
Exit For 'Exit the second For statement
End If
Next i
Next rng
Application.StatusBar = "Hiding duplicate rows"
If Not rngToHide Is Nothing Then rngToHide.EntireRow.Hidden = True
Application.StatusBar = False
End Sub

Iterative SUMIF Function Using VBA

Consider the following table:
What I would like to be able to do is create something like on the right hand side. This essentially requires telling Excel to sum all values for which the cell is zero until it encounters a 1, at which point it should begin the count again. I imagine this can be done using VBA, so I just need to determine how to actually set up that code. I imagine that the building blocks should be something like this:
Dim row As Long
Dim sum As List
row = Excel row definition
While ColB <> ""
If ColB.value = 0
Append ColC.value to Sum
Else Do Nothing
row = row + 1
Loop
Any help with the structure and syntax of the code would be much appreciated.
Try this:
Sub test()
Dim cel As Range, sRng As Range, oRng As Range, Rng As Range
Dim i As Long: i = 1
On Error GoTo halt
With Sheet1
.AutoFilterMode = False
Set Rng = .Range("B1", .Range("B" & .Rows.Count).End(xlUp))
Rng.AutoFilter 1, 0
Set sRng = Rng.Offset(1, -1).Resize(Rng.Rows.Count - 1) _
.SpecialCells(xlCellTypeVisible)
Rng.AutoFilter 1, 1
Set oRng = Rng.Offset(1, 0).SpecialCells(xlCellTypeVisible)
.AutoFilterMode = False
End With
If sRng.Areas.Count >= oRng.Areas.Count Then i = 2
For Each cel In oRng.Areas
If i > sRng.Areas.Count Then Exit For
If cel.Cells.Count = 1 Then
cel.Offset(0, 1).Formula = _
"=SUM(" & sRng.Areas(i).Address(True, True) & ")"
Else
cel.Cells(cel.Cells.Count).Offset(0, 1).Formula = _
"=SUM(" & sRng.Areas(i).Address(True, True) & ")"
End If
i = i + 1
Next
Exit Sub
halt:
Sheet1.AutoFilterMode = False
End Sub
Edit1:
Above works regardless of how many zero's or one's you have in Column B.
If error occurs, it will exit. I leave the coding on how you want the error handled.

Resources