I'm doing a data quality check on a large column of strings (ie. Last Name) and want to see if they contain a number.
The VBA code that I've tried so far should be straight forward: if the LastName field contains 1 or 2 (etc.) than the ReasonCode = 3. Later on, if ReasonCode = 3, the results spits out "Contains a number"
However, in situations like 'marshall', it's still populating "Contains a number"
ElseIf LastName Like "*#*" Or FirstName Like "*#*" Then
ReasonCode = 3
End If
ElseIf ReasonCode = 3 Then
Cells(RowT, 16).Value = "Contains a number"
ReasonCode = 0
End If
BigBen pretty much answered your question on the usage of # with the Like operator, so I won't really go into that. But what I did notice is your usage of many Like operators in your If statement, so I figured I would take the opportunity to share a function I created a while ago and use frequently.
The purpose of this function is to reduce multiple Like statements when comparing against a single value. While this function doesn't address your specific issue, it may be helpful in the future.
Function OrLike(ByVal compareVar As Variant, ParamArray CompareArgs() As Variant) As Boolean
Dim i As Long
If IsArray(CompareArgs(0)) Then
For i = LBound(CompareArgs(0)) To UBound(CompareArgs(0))
If compareVar Like CompareArgs(0)(i) Then
OrLike = True
Exit Function
End If
Next
Else
For i = LBound(CompareArgs) To UBound(CompareArgs)
If compareVar Like CStr(CompareArgs(i)) Then
OrLike = True
Exit Function
End If
Next i
End If
End Function
First, the function checks the first value used in CompareArgs. If this value is an array, then it compares against the array, otherwise it will utilize the ParamArray keyword. This allows you to use this function in two ways:
With an Array variable as your arguments
Dim Arr() As Variant
Arr = Array("Blah*", "Blah2*")
If Orlike("BlahBlah", Arr) Then
' . . .
End If
Utilizing ParamArray
If OrLike("BlahBlah", "Blah*", "Blah2*") Then
' . . .
End If
Obviously, you don't want to use your current If statement that you provided. But if you were as an example, take a look how this function simplifies your statement and vastly improves readability by turning this:
ElseIf LastName Like "*1*" Or LastName Like "*2*" Or LastName Like "*3*" Or LastName Like "*4*" _
Or LastName Like "*5*" Or LastName Like "*6*" Or LastName Like "*7*" _
Or LastName Like "*8*" Or LastName Like "*9*" Or LastName Like "*0*" Then
Into this:
ElseIf OrLike(LastName, "*1*", "*2*", "*3*", "*4*", "*5*", "*6*", "*7*", "*8*", "*9*", "*0*") Then
Not only does it improve readability, but it may actually increase performance. The problem with VBA If...Then statements is that everything in the line must be evaluated, even after a statement that returns True.
This function takes all of these arguments, and evaluates each statement until one becomes True, then it immediately exits the function - ignoring the remaining arguments.
Related
Here is a code of my VBA script:
Function custom_if_formula(condition)
MsgBox(condition)
End Function
I am pasting the formula to any cell:
=custom_if_formula(B1="something")
The result in MsgBox is: TRUE or FALSE. Is it possible get in MsgBox as a result B1="something" as instead?
A pseudo-code of what I would like to achieve:
Function custom_if_formula(condition)
condition = condition.formula 'any method which take a literal string
MsgBox(condition)
End Function
P.S. My goal is to implement my own IFS function who behave identically like in Excel 2016. I am just curious if its possible. Tha'ts why I don't want to pass a string as an argument.
I think you're looking for:
Function custom_if_formula(condition)
If condition.Value = "something" Then MsgBox "something"
End Function
Where B1 is taken as the argument: =custom_if_formula(B1). Putting this in any cell (when B1 contains the string "something") will return:
You should really clarify what your intent is here, though. A UDF should return a value to its cell. Right now, it will just say 0 in the UDF's cell. Additionally, looking for "something" could be interpreted as looking for anything, and using this kind of verbiage leads to the "Who's on first, what's on second" ordeal...
Ok, I found a way how to do it:
Function custom_if_formula(condition)
cell_formula = Range(Application.Caller.Address).Formula 'I get an adress of a cell which call an UDF, and then take all string it contains
arg = Mid(cell_formula, 12, 100)
MsgBox(arg)
End Function
Currently, My code does not properly detect if the string has a specific character.
for simple explanation. please refer to this:
Dim strSample as String = "Y1-K99"
and I tried this code if the string will detect if the strSample has a letter K.
If (strSample Like "##K*" Or strSample Like "###K*") Then
'Do Something
End if
But It does not trigger or go to the Inside Function of the If Else condition even if it has a K.
If you are after "strSample has a letter K"
, you will need to change your IF condition to be
If (strSample Like "*K*") Then
'Do Something
End if
Have a read at this
Like Operator
I am writing an Excel VBA program that validates a school course schedule. A key component is a global dictionary object that keeps track of the course number (the key) and the number of times that course is scheduled (the item). I have successfully created and loaded the dictionary. I'm trying to lookup the value associated with the course key, but have been unable to do so using the one-line examples I've found at this site. I'd like to use this line of code:
intCourseCnt = gdicCourses("BAAC 100")
or
intCourseCnt = gdicCourses.Item("BAAC 100")
but neither work (actually, the "BAAC 100" part is a string variable, but it won't even work if I hardcode a course in.) Instead, I have to use the kludgy loop code below to lookup the course count:
Private Function Check_Course_Dup_Helper(strCourse As String) As Boolean
Dim k As Variant
Check_Course_Dup_Helper = False
' Read thru dictionary. Look to see if only 1 occurrence then jump out.
For Each k In gdicCourses.Keys
If k = strCourse Then
If gdicCourses.Item(k) = 1 Then
Check_Course_Dup_Helper = True
Exit Function
End If
Exit Function
End If
Next
End Function
Is there a way to rewrite this so that I can lookup of the item value without the loop?
Thank you.
Thanks for the prompt replies. Answers below:
David, the gdicCourses("BAAC 100") code value while the program is running is "empty" which makes the receiving variable equal to 0. The result is the same if I use strCourse variable. Also, the dictionary populating code is shown below. I do not believe it is a problem because I can correctly access the values elsewhere in the program where For-Each-Next loops that use a range variable are employed. Whitespace and non-printable characters are not present.
My guess is that I need to use a range to reference the position in the dictionary rather than a string. I've tried pretty much every combination of this that I can think of, but the value is still "empty".
Set gdicCourses = New Scripting.Dictionary
For Each c In Worksheets("Tables").Range("combined_courses").Cells
If Not (gdicCourses.Exists(c)) Then
gdicCourses.Add c, (Application.WorksheetFunction.CountIF(Range("MWF_Table_Full"), c
(Application.WorksheetFunction.CountIf(Range("TTh_Table_Full"), c)))
End If
Next
I need to call a value of a variable as another variable. E.g.
I assign the FirstVariable = "One"
and then I asssign the Name as Text to
SecondVaribale = "FirstVariable" (Note here it is the "TEXT")
So now can I call or assign the SecondVariable to return the value as One in any ways?
Means this should return One:
Range("A1").Value = SecondVariable
is that possible?
Because I have about 40 such variables to be done in around 4 - 6 instances which I want to drive through a mapping sheet in Excel.
The easy way out is assigning the variables manually which would require manual intervention in future which I want to avoid.
You can create your own custom Dictionary or Collection in VBA for Excel 2007. Then you can "name" your variables, and use another string variable to indirectly access those "named variables". Choice of using Dictionary or Collection is how easy you need it to change the value of a "named variable".
A Dictionary allows you to add, read, change, and remove key/value pairs. A Collection only allows add, read, and remove; you have to use a subroutine to change a key/value pair. A Collection lets you use a numeric index (like an array) to access the key/value pairs; a Dictionary does not have an array-like feature. A pretty thorough comparison is at http://www.experts-exchange.com/Software/Office_Productivity/Office_Suites/MS_Office/A_3391-Using-the-Dictionary-Class-in-VBA.html
So to adapt your example, and to also show a change in value of a "named variable", here is some example code:
Public Function test() As String
' Dictionary example
Dim myDictionary, SecondVariable As String
Set myDictionary = CreateObject("scripting.dictionary")
myDictionary.Add "FirstVariable", "Four"
myDictionary.Add "AnotherVariable", "Two"
SecondVariable = "FirstVariable"
' note that "FirstVariable" must be already defined in the Dictionary else an error will occur; from your example this seemed to be the case
' if this was not the case then will need a more complex line using: If myDictionary.exists(SecondVariable) Then ... Else ...
myDictionary.Item(SecondVariable) = "One"
test = myDictionary.Item(SecondVariable) 'function returns "One"; the current value of "FirstVariable" in the Dictionary
End Function
Public Function test2() As String
' Collection example
Dim myCollection As New Collection, SecondVariable As String
myCollection.Add "Four", "FirstVariable"
myCollection.Add "Two", "AnotherVariable"
SecondVariable = "FirstVariable"
'myCollection(SecondVariable) = "One" 'Cannot do this with a Collection; have to use a Sub like the example below
Call setCollectionValue(myCollection, SecondVariable, "One")
test2 = myCollection(SecondVariable) 'function returns "One"; the current value of "FirstVariable" in the Collection
End Function
Private Sub setCollectionValue(collect As Collection, key As String, value As String)
On Error Resume Next
collect.Remove key
On Error GoTo 0
collect.Add value, key
End Sub
So this is a robust problem. I have a function which accepts 2 args (string_name, macros). Here it is so I can further explain.
function ParseStrings(string_name, macros)
return my_table[string_name]
-- All it does it returns the string_name's value
end
The problem is that the second arg is a table, and if it's a table then in the string there are going to be various parts that have the format "String stuff $MACRO_KEY; more string text" and the content between the $ and ; is the key to look up in the macro table sent with it. Now anytime a value like that appears in the string there will always be a second arg that's a table, so no problems their. I need to be able to count up the number of instances of macros in a string and then replace each macro component with it's respective macros' table value. So here's how the func is called in this instance...
local my_table = {
my_string = "My string content $MACRO_COMPONENT; more string stuff $MACRO_COMPONENT_SUB;$MACRO_COMPONENT_ALT;"
}
local macro = {
MACRO_COMPONENT = "F",
MACRO_COMPONENT_SUB = "Random Text",
MACRO_COMPONENT_ALT = "14598"
}
function ParseStrings(string_name, macros)
return my_table[string_name]
-- All it does it returns the string_name's value
end
ParseStrings("my_string", macro)
So I am thinking:
string.gsub(my_table[my_string]:match("%b$;"):sub(2,my_table[my_string]:match("%b$;"):len() - 1)
but this is a long and overtly complex answer (AFAIK) and from my tests it only does 1 replacement (because the pattern is only found once) and that's doesn't work well if there are multiple instances in the string. So ideas?