Dictionary lookup fails - excel

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

Related

Use variable within a function, or use the function itself?

I have this function within my code:
Function get_header(ByVal rw As Range) As Scripting.Dictionary
Dim header As New Scripting.Dictionary
Dim used As Range
Set used = Range(rw.Cells(1, 1), rw.Cells(1, rw.Cells(1, rw.Columns.Count).End(xlToLeft).Column))
For Each cl In used.Cells
header.Add cl.Value, cl.Column
Next
Set get_header = header
End Function
What the function does is it takes the table header and creates a dictionary of column names and the respective indexes, so that the order of columns is not important for the rest of the code.
My question is: is it necessary to use a separate variable to store the value throughout the loop, or can I
edit the returned value ("get_header") whole time instead of only passing the value at the end or
use the With structure like this:
Function get_header(ByVal rw As Range) As Scripting.Dictionary
Dim used As Range
With rw
Set used = Range(.Cells(1, 1), .Cells(1, .Cells(1, .Columns.Count).End(xlToLeft).Column))
End With
With get_header
For Each cl In used.Cells
.Add cl.Value, cl.Column
Next
End With
End Function
Also, why should I use any of these structures instead of the others?
Thanks for any advice, guys.
Wanted to comment this, but too long for a comment:
When I have a function that "calculates" the return value step by step, I usually prefer to have an intermediate variable for the following reasons (note that using extra variables comes at no cost during runtime, that's not an issue):
Naming: I don't like assigning values to something that is called getSomething.
when used right-sided, it avoids ambiguity between the "intermediate" value and a recursive call to the function: getSomething = getSomething + getSomething(p). The compiler can handle that, but my brain? Not so much. I find retVal = retVal + getSomething(p) is much clearer.
When running on an error condition in the middle of execution, I can easily do an Exit Function without thinking about what was already calculated.
But at the end of the day (and this valid also for the question if of if not to use a With-statement), there are two things that matters: (1) Which code is easier to read and understand. and (2) Which code is less likely to hold an error. As human brains don't work all the same: Find you personal style and stick to it.
I would favor your first example instead of editing the function's return value more than once in a single function call.
Depending on whether error trapping breaks or is silent (i.e. on error resume next), you may get different and unintended results. Importantly, you may get unintended results and not know it.
If a few cycles run fine, and the function result variable is updated a few times, then some error occurs with processing a later cycle (due to inputs, scope, range, etc issues for the later part of the input array, or even just memory or focus or other runtime issues), you will have a problem: the function returns a premature (therefore incorrect) result, but appears as though it has run properly.
I'd recommend coding to avoid this situation, whether or not errors are suppressed. An easy way to do this is to not set the function result object more than once per possible function execution path.
Your first code block seems to conform to this thinking already. Each of your alternate suggestions 1 and 2 do not.

Runtime Error 13 when TextBox containing a Date is empty

Despite of checking many questions relating error 13, I could not find answers to my problem, so I am giving a shot here:
I am building my code to save information from a userform, but first I am testing to see if mandatory textboxes are empty. Since I am using a 64 bits machine I have used Trevor Eyre´s CalendarForm.
However while testing the code I hit a problem with the empty textboxes that receives the dates from CalendarForms:
In this line:
Dim dteCompraDataOps As Date: dteCompraDataOps = Me.txtTesouro_Compra_DataOps.value
This part is highlighted and returns Runtime Error 13:
dteCompraDataOps = Me.txtTesouro_Compra_DataOps.value
When I check the values coming from empty TextBoxes I get:
dteCompraDataOps = "00:00:00"`
Which is correct since it should be treated as Date, but this:
Me.txtTesouro_Compra_DataOps.value = ""
Is coming as a string.
I did a little search and noticed that Date data types are tricky when the textbox they come from are empty.
I could find a solution: creating a Select Case to test the mandatory fields before declaring the variables but I would like o learn how to deal with the empty textboxes that are supposed to be empty.
Any chance you can shed some light into my conundrum?
Thanks in advance.
Cub4_RJ
There are two ways to handle this.
a) Check for Null:
With Me.txtTesouro_Compra_DataOps
If Not IsNull(.Value) Then
If IsDate(.Value) Then dteCompraDataOps = . Value
End if
End With
b) Introduce a new variable of Variant type which accepts everything (including nulls) and check it's value.
Dim rawData As variant
rawData = Me.txtTesouro_Compra_DataOps.Value
If Not IsNull(rawData) Then
If IsDate(rawData) Then dteCompraDataOps = rawData
End If
The problem with approach A, is that the value 123 is treated as a date, however option B will catch it.

Changes in a temporary variable are affecting the variable that feeds from

I'm designing a Mastermind game, which basically compares 2 lists and marks the similarities. When a colour is found at the right place, a flag making the correct position is added and the item found on the reference list is marked off. The reference list is feeding off an array from another function. The problem is at the mark off, as any changes done to the reference list is changing also the original array, which i don't want it to happen
tempCode = mCode #mCode is the array combination randomly generated from another function
for i in range (len(uCode)): #user input array
for j in range (len(tempCode)): #temp array
if uCode[i] == tempCode[j]: # compare individual chars
if i == j: #compare position
flagMark = "*"
tempCode.insert(j+1, "x") #problem starts here
tempCode.remove(tempCode[j])
fCode.append(flagMark)
When the insert is reached both the tempCode and mCode change which it is not intended.
The code is written in a way should the user enter a combination of the same colours, thus checking the chras(the colours are just letters) and the position, and then mark them of with "x"
As it stands, when it gets to
tempCode.insert(j+1, "x")
the arrays will change to
mCode = ["B","R","x","G","Y"]
tempCode = ["B","R","x","G","Y"]
when I would just want
mCode = ["B","R","G","Y"]
tempCode = ["B","R","x","G","Y"]
See also this answer, which is a different presentation of the same problem.
Essentially, when you do tempCode = mCode, you're not making a copy of mCode, you're actually making another reference to it. Anything you do to tempCode thereafter affects the original as well, so at any given time the condition tempCode == mCode will be true (as they're the same object).
You probably want to make a copy of mCode, which could be done in either of the following ways:
tempCode = mCode.copy()
tempCode = mCode[:]
which produces a different list with the same elements, rather than the same list

Trim and Dictionary can not work together

I have code like this, but:
If I use myDic.add Trim(cells(4,5)), key --> I get an error.
If I use myDic.add cells(4,5), key --> no error.
Dim MyDictionary As Scripting.Dictionary
Set MyDictionary = New Scripting.Dictionary
Do While Len(Temp.Cells(num, 1))) > 0
"myDic.add Trim(cells(4,5)), key" 'Error Here
Loop
Is that we cannot use Trim when we use Add ? Thanks
You can use Trim (or typed function Trim$) with dict.add. There are a number of other errors with your code:
Get rid of the " around
"myDic.add Trim(cells(4,5)), key"
i.e.
myDic.add Trim(cells(4,5)), key
Also, you add key first with dictionaries, though I don't know what you mean by key here. Where is it defined and do you mean key for the value you wish to add to the dictionary?
And, you don't increment the cell it is always cells(4,5) . If this was a key they must be unique.
Traditionally I would expect something like
dict.add key, Trim$(ws.cells(i,5))
where key is unique and defined, ws is a variable holding the parent sheet name, i (or maybe num?) is an Long type variable to allow you to change the value being added, assuming you want to add a cell value in a loop to a dictionary.

VBA Access Arguments Passing Values

Access 2013
I'm calling a formula to modify a string and it's changing the values w/in the parent sub.
Example:
Debug.Print Str 'Hello World my name is bob
BOBexists = InStringChceck(Str,"bob")
Debug.Print Str 'HELLO WORLD MY NAME IS BOB
Debug.Print BOBexists 'TRUE
I've used this function, InStringCheck, in Excel VBA before (and it's just an example, all of my string tools are doing this same thing now and I don't know why)
Function InStringCheck(Phrase as string, Term as string) as Boolean
Phrase = UCase(Phrase)
Term = UCase(Term)
if instr(1, Phrase, Term) then InStringCheck = True else InStringCheck = False
end function
In several of my functions I manipulate the input variables, to arrive at a solution, but I don't want those manipulations to persist outside of the function unless I pass them back up - some how they're being passed up, but they're not dimed as public variables
VBA parameters are implicitly passed by reference (ByRef). This means you're passing a reference to the value, not the value itself: mutating that value inside the procedure will result in that mutated value being visible to the calling code.
This is often used as a trick to return multiple values from a function/procedure:
Public Sub DoSomething(ByVal inValue1 As Integer, ByRef outResult1 As Integer, ...)
You have two options:
Pass the parameters by value (ByVal)
Introduce local variables and mutate them instead of mutating the paramters (and heck, pass the parameters ByRef explicitly)
If you have lots of occurrences of parameters being implicitly passed ByRef in your project, fixing them everywhere can easily get tedious. With Rubberduck you can easily locate all occurrences, navigate there, and apply appropriate fixes:
Disclaimer: I'm heavily involved in the Rubberduck project.
Building a little on #Sorcer's answer, VBA has default Sub/Functions parameters passing "by reference" (i. e.: "ByRef" keyword assumed if not specified) so that if you don't want their "inside" modifications survive outside them you have to explicitly type "ByVal" keyword before them in the arguments list.
But you have the option to avoid such modifications take place altoghether by using StrComp():
Function InStringCheck(Phrase as string, Term as string) as Boolean
InStringCheck = StrComp(Phrase, Term, vbTextCompare) = 0
End Function
Which could also lead you to avoid the use of InStringCheck() in favour of a direct use of StrComp() in your code

Resources