VBA - Range Object Sets Only Once in Loop - vba

I am writing code which matches a date (from a file), puts this into a collection and then attempts to find this on a spreadsheet. Once it finds it, it puts the following two items in the collection in the two cells. When I run this I get the following error: "Object variable or With block variable not set". I have attempted to debug my code and it shows that after the first loop of the code below, the range object, "rthecell", changes to the proper value. Once the second iteration of the loop occurs the value of "rthecell" changes to "Nothing".
Ex:
Set rtheCell = Range("A:A").Find(What:=LineItem1)
rtheCell.Offset(, 1).Value = LineItem3
rtheCell.Offset(, 2).Value = LineItem2
Set rtheCell = Nothing
Again, everything works as intended on the first iteration of the loop but I receive the error once the second iteration occurs.
Here is the full code:
Sub InputData()
'Declare variables
Dim sFilePath As String
Dim sLineFromFile As String
Dim saLineItems() As String
Dim element As Variant
Dim col As Collection
Dim LineItem1 As String
Dim LineItem2 As String
Dim LineItem3 As String
Dim rtheCell As Range
Set col = New Collection
'Insert file path name here, this file will be overwritten each morning
sFilePath = "P:\Billing_Count.csv"
Open sFilePath For Input As #1
Do Until EOF(1)
Line Input #1, sLineFromFile
'Split each line into a string array
'First replace all space with comma, then replace all double comma with single comma
'Replace all commas with space
'Then perform split with all values separated by one space
sLineFromFile = Replace(sLineFromFile, Chr(32), ",")
sLineFromFile = Replace(sLineFromFile, ",,", ",")
sLineFromFile = Replace(sLineFromFile, ",", " ")
saLineItems = Split(sLineFromFile, " ")
'Add line from saLineItem array to a collection
For Each element In saLineItems
If element <> " " Then
col.Add element
End If
Next
Loop
Close #1
'Place each value of array into a smaller array of size 3
Dim i As Integer
i = 1
Do Until i > col.Count
'Place each value of array into a string-type variable
'This line is the date
LineItem1 = col.Item(i)
i = i + 1
'This line should be the BW count make sure to check
LineItem2 = col.Item(i)
i = i + 1
'This line should be the ECC count make sure to check
LineItem3 = col.Item(i)
i = i + 1
'Find the matching date in existing Daily Billing File (dates on Excel must be formatted as
'general or text) and add ECC and BW counts on adjacent fields
Set rtheCell = Range("A3:A37").Find(What:=LineItem1)
rtheCell.Offset(, 1).Value = LineItem3 'This is LineItem3 since we can ECC data to appear before BW
rtheCell.Offset(, 2).Value = LineItem2
Set rtheCell = Nothing
LineItem1 = 0
Loop
'Format cells to appear as number with no decimals
'Format cells to have horizontal alignment
Sheets(1).Range("B3:C50").NumberFormat = "0"
Sheets(1).Range("C3:C50").HorizontalAlignment = xlRight
End Sub

when you use the Range.Find method, typically you would either use the After:= parameter in subsequent calls or use the Range.FindNext method which assumes After:= the last found item. Since you are not modifying the actual found cells' value(s) in any way, you need to record the original found cell (typically the address) because eventually you will loop back to the original.
dim fndrng as range, fndstr as string
set fndrng = Range("A:A").Find(What:=LineItem1, after:=cells(rows.count, "A"))
if not fndrng is nothing then
fndstr = fndrng.address
do while True
'do stuff here
set fndrng = Range("A:A").FindNext(after:=fndrng)
if fndstr = fndrng.address then exit do
loop
end if
That should give you the idea of looping through all the matching calls until you loop back to the original. tbh, it is hard to adequately expand on the small amount of code supplied.

Related

VBA code to only show rows that contain similar text to an input field?

I'm new to VBA and am trying to cobble together some code to allow a user to input a word (or several words) into a cell and then show a list of matching row entries.
I have tried the following code but am getting an "instring = type mismatch" error.
Note that "B3" is the field dedicated for the "search word" and column F is the column containing the text I want to search within. If the word is contained, I want to show that row and hide all rows that don't contain that word.
Sub Find_Possible_Task()
ROW_NUMBER = 0
SEARCH_STRING = Sheets("codeset").Range("B3")
ROW_NUMBER = ROW_NUMBER + 1
ITEM_IN_REVIEW = Sheets("codeset").Range("F:F")
If InStr(ITEM_IN_REVIEW, SEARCH_STRING) Then
Do
Cells(c.Row).EntireRow.Hidden = False
Loop Until ITEM_IN_REVIEW = ""
End If
End Sub
TIA!
Few bad coding conventions or even possibly downright errors:
It's a good practice to explicity declare the scope Public/Private of your Sub procedure
Unless you're passing the variables from some place else, they need to be declared with Dim keyword
Using Option Explicit will help you prevent aforementioned error(s)
(Subjective) variables in all caps are ugly and in most programming languages it is convention to reserve all caps variables names for constants (Const)
Option Explicit
Private Sub keep_matches()
Dim what As Range
Dim where As Range
Dim res As Range ' result
Dim lr As Long ' last active row
Dim ws As Worksheet: Set ws = Sheets("codeset")
lr = ws.Cells(ws.Rows.Count, "F").End(xlUp).Row
Set what = ws.Range("B3")
Set where = ws.Range("F1:F" & lr)
' we'll create an extra column for a loop in our .Find method
where.Copy
ws.Range("F1").EntireColumn.Insert
ws.Range("F1").PasteSpecial xlPasteValues
where.EntireRow.Hidden = True ' preemptively hide them all
Set where = ws.Range("F1:F" & lr)
Set res = where.Find(what, lookIn:=xlValues) ' ilook for matches, 1st attempt
If Not res Is Nothing Then ' if found
Do Until res Is Nothing ' repeat for all results
res.EntireRow.Hidden = False
res = "Checked"
Set res = where.FindNext(res)
Loop
Else
MsgBox("No matches were found")
where.EntireRow.Hidden = False ' we don't wanna hide anything
End If
ws.Range("F1").EntireColumn.Delete ' remove the extra help column for Find method
End Sub
Should work as expected.
If there are any question, let me know.
instead of instr(), consider range.find().
Sub Find_Possible_Task()
Dim SEARCH_STRING As String
Dim ITEM_IN_REVIEW As Range
Dim found As Range
Dim i As Integer
SEARCH_STRING = Sheets("Sheet1").Range("B3").Value
i = 1
Do
Set ITEM_IN_REVIEW = Sheets("Sheet1").Cells(i, 6)
Set found = ITEM_IN_REVIEW.Find(What:=SEARCH_STRING)
If found Is Nothing Then
ITEM_IN_REVIEW.EntireRow.Hidden = True
End If
i = i + 1
Loop Until ITEM_IN_REVIEW = ""
End Sub
alternatively, consider using filter table:
1. check if your table has filter on ==> if yes, pass. if no, turn on filter.
2. filter column F for keyword to contain value in cell B3.

How to set a different link in each cell in a range?

I'm programming a Macro in VB for Excel 2013 that search for coincidences in different worksheets, and add a link to the cells that match.
I'm havin torubles to insert the link in the cell, since the link must be different for a range of cells, I need help here.
Here is my code
Dim bufferDetails As String
Dim tmpCell As String
Dim spot As String
Dim cell As Variant
Dim cellSpots As Variant
For Each cell In Worksheets("MMS-Locations").Range("D2:D1833")
If (cell.Value2 = "NULL") Then
cell.Value2 = "NULL"
Else
tmpCell = cell.Text
'A62
If (Left(tmpCell, 3) = "A62") Then
spot = spotName(tmpCell)
For Each cellSpots In Worksheets("DetailedMap").Range("G60:CF123")
If (cellSpots.Value2 = spot) Then
For Each linkToSpot In Worksheets("MMS-Locations").Range("H2:H1833")
Worksheets("MMS-Locations").Hyperlinks.Add _
Anchor:=Range(linkToSpot), _
Address:="http://example.microsoft.com", _
ScreenTip:="Microsoft Web Site", _
TextToDisplay:="Microsoft"
Next linkToSpot
Debug.Print ("Encontrado " + cellSpots)
End If
Next cellSpots
End If
End If
Next cell
End Sub
Function spotName(fullName As String) As String
Dim realSpot As String
Dim lenght As Integer
lenght = Len(fullName) - 3
realSpot = Right(fullName, lenght)
spotName = realSpot
End Function
As I was thinking the linkToSpot variable contains the actual cell in the range, so I can move my selection of the sell, but my code fails in there with this error:
Error in the Range method of the '_Global' object,
Just for reference, here is what I use to convert a phone number to an email for texting..setting it as a hyperlink in the current cell.
ActiveCell.Value = myNumbr
Set myRange = ActiveCell
ActiveSheet.Hyperlinks.Add anchor:=myRange, Address:="mailto:" & myRange.Value, TextToDisplay:=myRange.Value`
Keep your code simple to start with, until you find a working script, then add other items. Make good use of the F8 key to step through your code to find out exactly where an error occurs.

Wrong value is returned when calling a function from a cell

First post yall.
Long story short, in Excel, when I call the following function (it's in its own module) from a cell, it returns the wrong value. The function returns the correct value when calling it from a sub, as well as when I step through the code (to the end), but the moment I call it from Excel, it returns a different value. Background at the bottom.
Things I've Tried
Making it a Public Function
Giving it an argument
Changing the function and/or module name
Moving it out of a module
Restarting Excel
A bunch of random stuff
It really is just this specific function that's giving me this issue, simpler functions do what they're told. I have to assume it has something to do with the order of events Excel is doing things, or the limits of what parts of Excel a function can change.
Function ActiveDisciplineFilters()
Application.Volatile 'makes the function update automatically
Dim disccolumn As Range
Dim uniquedisc() As String
Dim uniquediscstring As String
'create a string of unique values from the Discipline column
i = 0
If Range("LayerList[Discipline]").SpecialCells(xlCellTypeVisible).Address = Range("LayerList[Discipline]").Address Then
ActiveDisciplineFilters = "None"
Exit Function
End If
For Each cell In Range("LayerList[Discipline]").SpecialCells(xlCellTypeVisible)
If InStr(1, uniquediscstring, cell.Value) = 0 Then
If i <> 0 Then
uniquediscstring = uniquediscstring & ", " & cell.Value
Else
uniquediscstring = cell.Value
i = 1
End If
End If
Next
ActiveDisciplineFilters = uniquediscstring
End Function
Background
In Excel, I have a table. I'm taking all the data in one specific column of that table and creating a string of the unique values in that range (separated by comma). That string must be placed in another cell, for reasons I don't need to get into. If a filter is applied to the column, the unique values update automatically.
What would make Excel give me the right answer when I call it from a sub, then the wrong one when I call it from a cell?
Unfortunately, none of the SpecialCells methods work in a UDF. If you need this to be run from the worksheet as a formula, then your code should look like this instead:
Function ActiveDisciplineFilters()
Application.Volatile 'makes the function update automatically
Dim disccolumn As Range
Dim uniquedisc() As String
Dim uniquediscstring As String
Dim i As Long
Dim cell As Range
Dim bHidden As Boolean
'create a string of unique values from the Discipline column
i = 0
For Each cell In Range("LayerList[Discipline]").Cells
If cell.EntireRow.Hidden = False Then
If InStr(1, uniquediscstring, cell.Value) = 0 Then
If i <> 0 Then
uniquediscstring = uniquediscstring & ", " & cell.Value
Else
uniquediscstring = cell.Value
i = 1
End If
End If
Else
bHidden = True
End If
Next
If Not bHidden Then uniquediscstring = "None"
ActiveDisciplineFilters = uniquediscstring
End Function

Multi-dimensional array in VBA for Microsoft Word on Mac

I'm working on a macro to loop through a series of strings (a1, a2, a3) and replace them with a series of corresponding values (b1, b2, b3). I've created an array to store the strings to match:
Dim search_strings(1 To 2) As String
search_strings(1) = "match1"
search_strings(2) = "match2"
I can loop through this array with a For Each loop. But I can't figure out how to store and reference the corresponding replacement text. I know that I need some sort of key/value pair. I've tried using a dictionary, like this:
Dim dict As New Scripting.Dictionary
dict.Add "match", "replace"
But for that to work, I need to reference Microsoft Scripting Runtime, which isn't available on Mac OS X. (Currently, I get this error: Compile error: User-defined type not defined.)
Is there another way?
Here's the full code:
Sub MyMacro()
' Initialize variables
Dim search_strings(1 To 2) As String
Dim this_search_string As Variant
Dim myRange As Range
Dim Reply As Integer
' Define strings to match
search_strings(1) = "match1"
search_strings(2) = "match2"
' Run a search for each string in the array of strings to match
For Each this_search_string In search_strings
' Define the search range to be the whole document
Set myRange = ActiveDocument.Content
' Set the Find parameters
myRange.Find.ClearFormatting
myRange.Find.MatchWildcards = True
' Loop through each match in the document
Dim cached As Long
cached = myRange.End
Do While myRange.Find.Execute(this_search_string)
myRange.Select
' Prompt the user to replace the match
Reply = MsgBox("Replace '" & myRange.Find.Text & "'?", vbYesNoCancel)
If Reply = 6 Then ' "Yes" clicked
myRange.Text = "replacement"
ElseIf Reply = 2 Then ' "Cancel" clicked
Exit Do
End If
myRange.Start = myRange.Start + Len(myRange.Find.Text)
myRange.End = cached
Loop
Next this_search_string
End Sub
This may be completely off and I may look like a fool but have you tried using a two dimensional array so you can store the values with their replacements in the two dimensional array. Then you can loop through to get the replacement values. That is just an idea I had, it could be way off.
Sub MyMacro()
' Initialize variables
Dim search_strings(1 To 2, 1 to 2) As String
Dim this_search_string As Variant
Dim myRange As Range
Dim Reply As Integer
' Define strings to match
search_strings(1, 1) = "match1"
search_strings(2, 1) = "match2"
search_strings(1, 2) = "result"
search_strings(2, 2) = "result"
' Run a search for each string in the array of strings to match
For Each this_search_string In search_strings
' Define the search range to be the whole document
Set myRange = ActiveDocument.Content
' Set the Find parameters
myRange.Find.ClearFormatting
myRange.Find.MatchWildcards = True
' Loop through each match in the document
Dim cached As Long
cached = myRange.End
Do While myRange.Find.Execute(this_search_string)
myRange.Select
' Prompt the user to replace the match
Reply = MsgBox("Replace '" & myRange.Find.Text & "'?", vbYesNoCancel)
If Reply = 6 Then ' "Yes" clicked
myRange.Text = search_strings(1, 2)
ElseIf Reply = 2 Then ' "Cancel" clicked
Exit Do
End If
myRange.Start = myRange.Start + Len(myRange.Find.Text)
myRange.End = cached
Loop
Next this_search_string
End Sub
Im really not sure if this is what you are looking for so I apologize if I am wasting your time.

Looping through an array in VBA

In Sheet1, cell A1 I have the following text: A-B-C-D-E-F
I need to loop through this text and I have written the following code that works fine:
dim w as worksheet
dim s as variant
dim p as integer
set w = Worksheets(1)
p = 0
For Each s In Split(w.Range("A1").Value, "-")
p = p + 1
MsgBox Split(w.Range("A1").Value, "-")(p - 1)
Next s
The above code pops up the Message box showing each of the letters one after the other, as expected.
BUT I am not happy with the repeating of Split(w.Range("A1").Value, "-"), declaring the array for the loop and once again for every occurrence within the loop.
So I have tried with:
MsgBox s.Value
but it throws an error about an object being requested.
Why can I not use the Value property given that "s" is a variant?
Split returns a string array which is separated into individual strings by the For Each, so you can just pass those to MsgBox as is.
So, rather than using s.Value, just use s on its own.
In other words:
dim w as Worksheet
dim s as Variant
set w = Worksheets(1)
For Each s In Split(w.Range("A1").Value, "-")
MsgBox s
Next
You can do this like this
dim w as worksheet
dim s as string
set w = Worksheets(1)
For Each s In Split(w.Range("A1").Value, "-")
MsgBox s
Next
When you assign any object to a variant then it behaves like the object assigned to it and has only those properties which are present in object. In your For Each split you assigned string to s and string variables return their value directly and not by objString.Value.