InStr not working due to a loop on the Dim - excel

I am slowly building off this code as I'd like to learn VBA for future use. The purpose of this part of my code is to see if one cell's value is contained inside another sheet in the same workbook's cell. If this is true then I would like to mark column 10 of the same row in Worksheets("FDSA") as "ok" and write nothing if it is not.
Sub test1()
Dim Str As String
Dim Search As String
Str = Cells(2, 5).Value
Search = Worksheets("FDSA").Cells(2, 5).Value
If InStr(Search, Str) > 0 Then
Worksheets("FDSA").Cells(2, 10).Value = "ok"
End If
End Sub
I got an answer yesterday that helped a lot (top). I am now trying to make a loop for that code. Here it is now (bottom).
Sub test1()
For X = 2 To 5
For Y = 2 To 5
Dim Str As String
Dim Search As String
Str = Cells(X, 5).Value
Search = Worksheets("FDSA").Cells(Y, 5).Value
If InStr(Search, Str) > 0 Then
Worksheets("FDSA").Cells(Y, 10).Value = "ok"
Else
Worksheets("FDSA").Cells(Y, 10).Value = ""
End If
Next Y
Next X
End Sub
I ran a test to see if it worked and it did not. I made it so that cells(5,5) was not contained inside of Worksheets("FDSA").Cells(Y, 5) and it still wrote the ok. After doing some research I have concluded that the Dim storage might be the problem(It is using the value of the initials which due match).I tried using ReDim and clear but could not run the code. Thank you for your help, please let me know if you need more explanation.
-Alexis M.

I think you only want one loop:
Sub test1()
Dim Str As String
Dim Search As String
Dim X as Long
For X = 2 To 5
Str = Cells(X, 5).Value
Search = Worksheets("FDSA").Cells(X, 5).Value
If InStr(Search, Str) > 0 Then
Worksheets("FDSA").Cells(X, 10).Value = "ok"
Else
Worksheets("FDSA").Cells(X, 10).Value = ""
End If
Next X
End Sub

Related

Doouble Looopiing

I am trying to print out "OK" value if the statements same value with "NumberPallete" but my code doesn't work right.
I have two conditions to compare from one cell value ["54# / 221"]. The first condition value for "SeriesNumber" is [88] and then the Second condition value for "NumberPallete" is [221#]. I am using looping for "SeriesNumber" and "NumberPallete" to find the value because I have long data in the table sheet.
and then from the different sheets using looping too, I am starting with the First condition checks "SeriesNumber" value if the value is right, then check the second condition from "NumberPallete" value, in this condition, I want a print out "ok" value but "ok" value doesn't print out.
I am sorry, my English is poor. I'm trying my best to explain. Please help me.
Dim NumberPallete As String
Dim SeriesNumber As String
Dim I As Long
Dim j As Long
Dim z As Long
i = Cells(Rows.Count, 15).End(xlUp).Row
For j = 6 To i
'Cells(j, 20).Value = Right(Cells(j, 15).Value, Len(Cells(j, 15)) - InStr(1, Cells(j, 15).Value, "/"))
SeriesNumber = Right(Cells(j, 15).Value, Len(Cells(j, 15)) - InStr(1, Cells(j, 15).Value, "/"))
'Cells(j, 21).Value = Left(Cells(j, 15).Value, Len(Cells(j, 15)) - InStr(1, Cells(j, 15).Value, "/"))
NumberPallete = Left(Cells(j, 15).Value, Len(Cells(j, 15)) - InStr(1, Cells(j, 15).Value, "/"))
If SeriesNumber = 221 Then
For z = 4 To 250
If Worksheets("AAA").Cells(z, 2).Value = NumberPallete Then
Worksheets("AAA").Cells(z, 6).Value = "OK"
End If
Next z
Else
MsgBox ("Not OK")
End If
Next j
I may not have fully understood what you are trying to do but the code below is doing something and, hopefully, it can be fixed to do what you want.
Sub FindPalletNumber()
' 062
' you can find code to enter 2 values with input boxes at this link:-
' https://stackoverflow.com/questions/62651211/vba-excel-search-in-excel-specific-word-and-delete-all-rows-who-does-not-have-t
Dim Snum As Integer ' serial number
Dim Pnum As Integer ' pallet number
Dim Txt As String ' message text
Snum = 221 ' number only
Pnum = 54 ' no # sign, no brackets
If MarkOK(Snum, Pnum) Then
Txt = "Found and marked."
Else
Txt = "No match found."
End If
MsgBox Txt, vbInformation, "Action report"
End Sub
Private Function MarkOK(Snum As Integer, _
Pnum As Integer) As Boolean
' 062
' return True if found and marked
Const Pallet As Long = 0 ' element of array Nums
Const Serial As Long = 1 ' element of array Nums
Dim Nums() As String ' split cell pattern "54# / 221"
Dim Done As Boolean ' True if found
Dim R As Long ' loop counter: Row in ActiveSheet
Dim R2 As Long ' loop counter: Row in Ws("AAA")
For R = 6 To Cells(Rows.Count, 15).End(xlUp).Row
Nums = Split(Cells(R, 15).Value, "/")
' Nums(Pallet) = "54# ", Nums(Serial) = " 221"
If Val(Nums(Serial)) = Snum Then
With Worksheets("AAA")
For R2 = 4 To .Cells(.Rows.Count, 2).End(xlUp).Row
If .Cells(R2, 2).Value = Trim(Nums(Pallet)) Then
.Cells(R2, 6).Value = "OK"
Done = True
Exit For
End If
Next R2
End With
End If
If Done Then Exit For ' stop search if found
Next R
MarkOK = Done
End Function
In the first procedure the Pallet and Serial numbers should be set (Pnum and Snum). Then, when you run that procedure, it will call the other one which reports back whether a match was found or not. I have added a link where you can find code to get the two values from Input boxes, if that is what you need.
The function looks for the serial number in the ActiveSheet. If found, it looks for the pallet number in Sheet("AAA"). This is confusing because it looks for the pallet number found in the ActiveSheet, not the pallet number specified in the search. The pallet number in the search specs ends up not being used at all. Perhaps it's not needed.
Anyway, when the pallet is found the row is marked and the search terminates. If the pallet number isn't found the loop in the ActiveSheet is continued to look for another instance of the serial number. Note that the code is not enabled to find multiple pallets with the same serial number.

how to not enter if statement inside a loop if it have been executed

I have a for loop, and inside it i have if statement.
In my Excel I have a list that contains each value one time. Once I found it i don't want the code to even check the conditional, i want it to skip this part of the if statement completely each time the loop is executed, is it possible?
Here is my code and list:
the first iteration of the loop will find that "c" is the value so it will do what inside it (xc = i)
I don't want the code to even check "ElseIf Cells(1, i) = "c" again, like the following image, is this possible?
code as text:
Sub test()
Dim i, xa, xb, xc As Integer
For i = 1 To 5
If Cells(i, 1) = "a" Then
xa = i
ElseIf Cells(i, 1) = "b" Then
xb = i
ElseIf Cells(i, 1) = "c" Then
xc = i
End If
Next i
End Sub
My initial interpretation of your need was "if the code hits 'c' again, just don't act".
To do so, you could modify the logic as follows:
ElseIf (xc = 0) And (Cells(i, 1) = "c") Then
This way, as soon as xc is set, the first boolean expression would be False, and the overall condition would not ever be met again. As mentioned by #TimWilliams, VBA would still evaluate the second boolean expression, unlike other languages that feature short-circuiting options. #Gene's answer describes a way around this. Typically, for better performance, you would evaluate the simple conditions first, before resorting to costly ones.
Additional notes
In VBA, you must give a type to each variable. In your Dim line, only xc is an Integer, while the other variables are Variants.
An unqualified Cells() call operates on the currently active worksheet, which might not be the expected one. Suggestion: qualify Cells() with the CodeName of your worksheet. The CodeName is what you see or specify under a worksheet's (Name) property as seen from the Visual Basic editor. For example, if (Name) is Sheet1, use Sheet1.Cells(). This will only work if the code resides in the same workbook as Sheet1. If the code is behind the worksheet itself, you can even use Me.Cells().
When dealing with cell values as your code does, VBA is (silently) being nice and understands that, among the numerous properties of the Range class, Value is what you are interested in. It is better practice, however, to explicitly state the target property, such as in Sheet1.Cells(i, j).Value.
EDIT
Knowing the values will be distinct and that there are about 60 of them, I suggest you simply use a Dictionary, as shown below, to get each value's row in one go, without a cascade of Ifs:
Option Explicit
Sub test()
Dim i As Integer
Dim dict As Object 'Scripting.Dictionary
Set dict = CreateObject("Scripting.Dictionary")
For i = 1 To 5
dict(Cells(i, 1).Value) = i
Next
Debug.Print dict("a") '4
Debug.Print dict("b") '2
Debug.Print dict("c") '1
'Etc.
End Sub
if i understood your question you can try this code:
Sub test()
Dim i, xa, xb, xc As Integer
Dim a, b, c As Boolean
a = False
b = False
c = False
For i = 1 To 5
If Cells(i, 1) = "a" And a <> True Then
xa = i
a = True
ElseIf Cells(i, 1) = "b" And b <> True Then
xb = i
b = True
ElseIf Cells(i, 1) = "c" And c <> True Then
xc = 1
c = True
End If
Next i
End Sub
Boolean variable is setted true for example only when the cells(i,1)="a" and after the next "a" value are skipped...
hope this helps
I just wanted to "mod" Ferdinando's code so it's a bit more "readable", I think. The main (the substantive) difference between this version and Ferdinando's or Excelosaurus' is that the cell is not even tested once the value is detected. Remember that the question was: I don't want the code to even check "ElseIf Cells(1, i) = "c" again... So, this version does exactly that.
Sub test()
Dim i As Integer, xa As Integer, xb As Integer, xc As Integer
Dim aFound As Boolean, bFound As Boolean, cFound As Boolean
Dim r As Range
For i = 1 To 5
Set r = Cells(i, 1)
If Not aFound Then
If r = "a" Then xa = i: aFound = True
ElseIf Not bFound Then
If r = "b" Then xb = i: bFound = True
ElseIf Not cFound Then
If r = "c" Then xc = i: cFound = True
End If
Next i
End Sub
I don't like the idea of 60 ElseIfs. Please examine the code below. In order to test it, create a worksheet called "TestSheet" and enter your A1:A5 to cells H2:H6.
Sub TestSpike()
' 06 Jan 2019
Dim Rng As Range
Dim Items As Variant
Dim Spike As String
Dim Tmp As String
Dim i As Integer
Dim R As Long
Items = Split("c|b|0|a|1", "|")
With Worksheets("TestSheet").Columns("H")
For R = 2 To 6
Tmp = CStr(.Cells(R).Value)
If InStr(1, Spike, Tmp, vbTextCompare) = 0 Then
Spike = Spike & "|" & Tmp
On Error Resume Next
i = Application.WorksheetFunction.Match(Tmp, Items, 0)
If Err Then
MsgBox Tmp & " wasn't found in Array"
Else
MsgBox "i = " & i & " = Item " & Tmp
End If
End If
Next R
End With
End Sub
The code has a "Spike". Each item is first checked against the Spike. If it is found there no further tests are carried out. Else, it is added to the Spike.
New items, after being added to the Spike, are checked against the Array "Items" which would hold your 60 elements, separated by Chr(124) thus, Split("c|b|0|a|1", "|"). I use the worksheet function MATCH to look for the item in the array. The result is an index number (or an error, if not found). You can use this index number in a Select Case statement to process each item distinct from others, basically the same way as you now process it when the If statement returns True.
One idea you may find useful with this kind of setup is to use the index from the Match function to return a value from another array. The other array might, for example, contain function names and you use Application.Run to call a different function for each item. This would run significantly faster than examining 60-odd Select Case statements.

VBA text loop optimisation - Extract emails from text

I need a bit of help with a small project. I just started VBA and I think I could use learning to optimise my code.
Cell A2, contains a text with many email address separated by ",". I managed to extract all the email addresses but I think I made too much use of cells, and I was wondering if you can help me reduce that and use the variables defined instead.
Screenshot of the working code
Sub fpor()
Dim Text As String
Dim full As Integer
Dim i As Integer
Dim e As Integer
Dim part As String
Dim part_len As Integer
Dim part_a As Integer
Dim Text_2 As String
x = 5
ActiveCell = Range("A2")
Text = Range("A2")
full = Len(Text)
'full = InStrRev(Text, ",")
For i = 1 To full
Cells((x + i), 1).Value = Text
part = InStr(Text, ",")
Cells((x + i), 2).Value = part
Cells((x + i), 3) = Left(Text, part)
Cells((x + i), 4) = full - part
Text = Right(Cells((x + i), 1), Cells((x + i), 4))
If part = 0 Then
full = 0
Cells((x + i), 3) = Text
Exit For
Else:
full = Len(Text)
End If
Next i
MsgBox (full)
MsgBox (part)
End Sub `
How do you think I can better optimise the For Loop?
Thank you all for your answers you awesome people : )
you can greatly simplify your code with the use of Split() Function as follows:
Option Explicit
Sub fpor()
Dim emailsArr As Variant
With Worksheets("emails") '<--change "emails" with your actual sheet name
emailsArr = Split(.Range("a2"), ",") '<--| split all emails names delimited by a ',' into an array
.Range("A6").Resize(UBound(emailsArr)).value = Application.Transpose(emailsArr) '<--| write array content from cell A6 downwards
End With
End Sub

Linking multiple values in a cell to checkboxes in userform

I have the following search code:
Private Sub Search_Click()
Dim Name As String
Dim f As Range
Dim r As Long
Dim ws As Worksheet
Dim s As Integer
Dim FirstAddress As String
Name = surname.Value
With ws
Set f = Range("A:A").Find(what:=Name, LookIn:=xlValues)
If Not f Is Nothing Then
With Me
firstname.Value = f.Offset(0, 1).Value
tod.Value = f.Offset(0, 2).Value
program.Value = f.Offset(0, 3).Value
email.Value = f.Offset(0, 4).Text
officenumber.Value = f.Offset(0, 6).Text
cellnumber.Value = f.Offset(0, 7).Text
if f.offset(0,5).value = "PACT" then PACT.value = True
I want to modify the if f.offset(0,5).value: If I have one single value in a cell then it's easy to pick that one cell, link it to the checkbox and have the checkbox checkmarked. The problem is I have multiple values in the cell.
The values in the column 6 (f.offset(0,5).values) have the names as the checkboxes eg., checkboxes.name & " " so... PACT OTTAWA VANCOUVER MONTREAL TORONTO
This is done through an add command.
So the column 6 can display PACT OTTAWA VANCOUVER or PACT TORONTO or TORONTO depending on which checkboxes are clicked to add in userform.
If I can read and link to the name of the checkboxes and checkmark the ones that are in column 6 then that would work.
if f.offset(0,5).value = "PACT" then PACT.value = True
If I do it this way, then I'll have way too many combinations that I have to figure it out using if statements. Is there a way to "read" column 6, match it to the names of checkbox and checkmark the checkboxes in the userform when I click the search button?
EDIT:
Private Sub Search_Click()
Dim Name As String
Dim f As Range
Dim r As Long
Dim ws As Worksheet
Dim s As Integer
Dim FirstAddress As String
Dim str() As String
Name = surname.Value
With ws
Set f = Range("A:A").Find(what:=Name, LookIn:=xlValues)
If Not f Is Nothing Then
With Me
firstname.Value = f.Offset(0, 1).Value
tod.Value = f.Offset(0, 2).Value
program.Value = f.Offset(0, 3).Value
email.Value = f.Offset(0, 4).Text
officenumber.Value = f.Offset(0, 6).Text
cellnumber.Value = f.Offset(0, 7).Text
str() = Split(inputstr, " ")
For i = 0 To UBound(str)
Select Case UCase(Trim(str(i)))
Case "PACT": PACT.Value = True
Case "PrinceRupert": PrinceRupert.Value = True
Case "Montreal": Montreal.Value = True
Case "TET": TET.Value = True
Case "WPM": WPM.Value = True
Case "TC": TC.Value = True
Case "US": US.Value = True
Case "Other": Other.Value = True
End Select
Next i
I've added the following code but nothing happens.
I've searched on msdn but I don't quite understand the str() = split(inputstr, " ") and for I = 0 Ubound (str). I'm assuming the trim function is if after trimming the values of column 6 and case is "PACT" then PACT.value = True?
EDIT2:
It seems the code doesn't catch the words other than PACT and I'm assuming it picks up PACT because it's the first one. I've tried manually inputting the column 6 values and see if any names get picked up but it doesn't. Is there a way to fix this?
Perhaps it's because when I trim the data in column 6, it's seen as PACTPrinceRupertTETWPMTCUSOther. Is that why it won't pick it up?
Just as Siddharth said, Using split() and looping through the resulting array...
Dim str() As String
str() = Split(inputstr, " ")
For i = 0 To UBound(str)
Select Case ucase(trim(str(i)))
Case "PACT": pact.Value = True
case ....:
End Select
Next i

Excel VBA populate listbox with data from sheet using only rows that match variable

I'm building an Excel program at work so that collegues can view their upsells. What i'm stuck on is the population of the listbox.
When I populate the listbox it returns all rows. What I need it too return though is all rows where the name in the E column matches with the one contained in the public variable.
I searched all over to try and find a solution but have not had any luck.
Any help would be greatly appreciated as this is starting to bug me now.
Public Sub UserForm_Initialize()
var_associate = "Kirsty"
Dim var_nextline As Integer
var_nextline = 1
x = "a"
Do While x <> ""
x = Cells(var_nextline, 2)
If x <> var_associate Then
var_nextline = var_nextline + 1
Else
Me.lsb_upsell.AddItem(var_nextline, 2).Value
var_nextline = var_nextline + 1
End If
Loop
End Sub
x = Cells(var_nextline, 2)
should give an error, as the right-hand side returns the cell object, not the value contained in the cell, and assignment of objects must be done by using Set. Anyway, it is not what you want to have. Try
x = Cells(var_nextline, 2).Value2
Remark: You could improve your code:
Public Sub UserForm_Initialize()
Dim var_associate As String
var_associate = "Kirsty"
Dim var_nextline As Integer
var_nextline = 1
Dim x As String
Do
x = Cells(var_nextline, 2).Value2
If x = "" Then Exit Loop
If x = var_associate Then
Me.lsb_upsell.AddItem(x).Value
End If
var_nextline = var_nextline + 1
Loop
End Sub
Second remark: Where is Cells initialized (e.g. with Set Cells = ActiveSheet.Cells)? Add Option Explicit at the top of your file in order to get error messages for undeclared variables. Do you mean something like
ActiveSheet.Cells(var_nextline, 2).Value
?

Resources