Some sort of meta data for Dictionary - vba

I have set up a dictionary with a long list of keys and associated items. The user enters a string of keywords via an input box and vba splits each word into an array, finds the key and adds the item to an output string. For example:
User types in "Dinner Applebee's Restaurant", the code splits the string into three words, then makes a number code where each word is represented by a two-digit number "10 08 70" (spaces added to emphasize number schema).
Here's the problem, "Applebee's Restaurant" flows a little better in English than "Restaurant Applebee's" so the user would most likely type the words in that order but the number should really reflect "Restaurant Applebee's." Like it should be category then place instead of place then category. The number code should actually be "10 70 08."
Is there a way to group dictionary definitions without making a separate dictionary? Or evaluate each word and sort them into a particular order? As in the first two digits will always be greater than 10, the second two digits will also be greater than 10 or will be 00. The third two digits will always be less than 10. So as long as I know what word has less than 10, that number can be last.
It would be easy enough to just type the words in the correct order, but I fear the lay person hardly ever takes the easy route and there is a number of people who will use this, each at their own questionable level of computer-literacy.
An example of code was requested so here it is:
Dim dict As New Scripting.Dictionary
Dim InStr As String ' Input string
Dim Split() As String ' Array to hold words
Dim SegNum As String ' Output string
Dim I As Integer
' Adding searchable items to dictionary
' First two digits
dict.Add Key:="Dinner", Item:=10
dict.Add Key:="Lunch", Item:=15
dict.Add Key:="Breakfast", Item:=20
dict.Add Key:="Snack", Item:=50
' Second two digits
dict.Add Key:="Restaurant", Item:=70
dict.Add Key:="Home Cooked", Item:=80
' Third two digits
dict.Add Key:="Home", Item:=Format(0, "00)
dict.Add Key:="McDonald's", Item:=Format(1, "00")
dict.Add Key:="Burger King", Item:=Format(2,"00")
dict.Add Key:="Wendy's", Item:=Format(3, "00")
..
dict.Add Key:="Applebee's", Item:=Format(8, "00")
SplitStr = Split(InputBox("Please use some keywords to detail what you ate"), " ")
For I = LBound(SplitStr) To UBound(SplitStr)
SegNum = SegNum & dict(SplitStr(I))
Next
MsgBox SegNum

Here's an approach. You'd need to add in checks for two entries from the same category though.
And you have "home cooked" as a value - that will get broken up by Split()
Sub Tester()
Dim dict As New Scripting.Dictionary
Dim InStr As String ' Input string
Dim SplitStr() As String ' Array to hold words
Dim SegNum As String ' Output string
Dim I As Long, srt As Long, wd
dict.CompareMode = TextCompare
srt = 1
dict.Add Key:="Dinner", Item:=Array(10, srt)
dict.Add Key:="Lunch", Item:=Array(15, srt)
dict.Add Key:="Breakfast", Item:=Array(20, srt)
dict.Add Key:="Snack", Item:=Array(50, srt)
' Second two digits
srt = 2
dict.Add Key:="Restaurant", Item:=Array(70, srt)
dict.Add Key:="HomeCooked", Item:=Array(80, srt)
' Third two digits
srt = 3
dict.Add Key:="Home", Item:=Array(0, srt)
dict.Add Key:="McDonald's", Item:=Array(1, srt)
dict.Add Key:="Burger King", Item:=Array(2, srt)
dict.Add Key:="Wendy's", Item:=Array(3, srt)
dict.Add Key:="Applebee's", Item:=Array(8, srt)
SegNum = String(6, " ")
SplitStr = Split("dinner home homecooked")
'SplitStr = Split(InputBox("Please use some keywords to detail what you ate"), " ")
For I = LBound(SplitStr) To UBound(SplitStr)
wd = Trim(SplitStr(I))
If dict.Exists(wd) Then
srt = dict(wd)(1)
Mid(SegNum, 1 + ((srt - 1) * 2), 2) = Format(dict(wd)(0), "00")
End If
Next
MsgBox SegNum
End Sub

Related

ppt vba multiple slide selection macro error

When multiple PowerPoint slide numbers are entered in the input box (ex: 3, 5, 6), I want to create a macro that selects the slides of the entered number, but an error occurs.
Sub test()
Dim strresponse2 As String
Dim iresponse2 As String
strresponse2 = InputBox("page number" & vbCr & "ex) 2,4,11,5")
If IsNumeric(strresponse2) Then
iresponse2 = strresponse2
End If
ActiveWindow.Selection.Unselect
ActivePresentation.slides.Range(Array(iresponse2)).Select
'error here
'How to fix it so it doesn't get an error
'ActivePresentation.Slides.Range(Array(2, 4, 11,5)).Select
'no error
End Sub
Several issues here.
a) If you enter 2, 4, 5, the check for IsNumeric(strresponse2) will fail because the function tries to convert the whole string into one single number.
b) Array(iresponse2) will not convert the string into an array (of 3 numbers). It will convert the single string 2, 4, 5 into an string array with 1 (not 3) member.
In your case, you can use the Split-function to split the input string into an array of strings.
c) If you want to access the slides by number, the input needs to be of a numeric type, not of string (even if the strings contain numbers). You will need to convert the string array into a numeric array (if you pass a string or an array of strings as parameter, VBA will look for members with the name, not the index).
Have a look to the following piece of code and check if it does what you need - it's only half tested (as I have no Powerpoint VBA available, only Excel, but the priniple is the same)
Dim answer As String
answer = InputBox("page number" & vbCr & "ex) 2,4,11,5")
Dim pagesS() As String
pagesS = Split(answer, ",") ' Split the answer into an array of strings.
ReDim pagesN(0 To UBound(pagesS)) As Long ' Create an empty numeric array
Dim countS As Long, countN As Long
For countS = 0 To UBound(pagesS) ' Loop over all strings
If IsNumeric(pagesS(countS)) Then ' String is Numeric
Dim pageNo As Long
pageNo = Val(pagesS(countS)) ' Convert string to number
If pageNo > 0 And pageNo <= ActivePresentation.slides.Count Then
pagesN(countN) = pageNo ' When number is within valid range, copy it
countN = countN + 1 ' Count the number of valid page numbers
End If
End If
Next countS
If countN > 0 Then ' At least one number found
ReDim Preserve pagesN(0 To countN - 1) ' Get rid of unused elements
ActivePresentation.Slides.Range(pagesN).Select
End If

EXCEL: Extracting Milliletres and Litres from cell using vba or formula

I am trying to extract the Milliliters and Liters from text(Only the value, not the ml or Ltr), but as there is no specific standard, it is difficult to do a formula. Below is some examples of the types of descriptions I have:
- Settlement 2.5% (Settlement Discount)
- BAGDOL/1 (U-POL DOLPHIN GLAZE Brushable Stopper Bag 440ml)
- P38/5 (ISOPON P38 LIGHTWEIGHT FILLER 3Ltr)
- EGC21TT (EUROPEAN COATINGS PRIMER 4:1 HS 4Ltr TRIPLE TIGHT)
- RLT/1KIT (U-POL RAPTOR TINTABLE 750ml & 250ml STANDARD HARDENER KIT)
- CCWP/AL (U-POL CUSTOM CAN Pregassed Aerosol 400ml (Waterbased))
I have used the formula below which I feel I have over-complicated.:
=IF(LEN(IFERROR(MID(G2,FIND("ml",G2)-3,LEN(G2)),""))=0,IFERROR(SUBSTITUTE(MID(G2,FIND("Ltr",G2)-2,LEN(G2)),"Ltr)","")*1000,""),IFERROR(SUBSTITUTE(MID(G2,FIND("ml",G2)-3,LEN(G2)),"ml)",""),""))
So those with 440ml returns 440 and those that say 3Ltr return 3000 - But this is only if it is at the end of the cells text.
I want to be able to extract the value of the ml or Ltr no matter where it is in the text. Those that have more than one set of ml value in it, I want to sum them. Example : 750ml & 250ml should be 1000. Settlement 2.5% (Settlement Discount) should return 0.
Is there a simple way of doing this ? Id be happier with doing it in VBA, as I think this would be less complicated.
Any help would be appreciated!
There is probably a far better way of doing this using RegEx, but I'm not practiced in those dark arts so I'd use something like the following to get what you need:
Function getvolume(txt As String) As Long
Dim wrd As Variant, wrds As Variant
getvolume = 0
If txt = "" Then Exit Function
' strip out characters that will confuse
txt = Replace(txt, "(", " ")
txt = Replace(txt, ")", " ")
' convert all to lower case
txt = LCase(txt)
' split the text into individual "words"
wrds = Split(txt, " ")
For Each wrd In wrds
If wrd Like "*ml" Then getvolume = getvolume + Val(Replace(wrd, "ml", ""))
If wrd Like "*ltr" Then getvolume = getvolume + 1000 * Val(Replace(wrd, "ltr", ""))
Next
End Function
So if your text is in G2 then you'd use a formula of =getvolume(G2)
regex User Defined Function
Option Explicit
Function metricMeasure(str As String)
Dim n As Long, unit As String, nums() As Variant
Static rgx As Object, cmat As Object
'with rgx as static, it only has to be created once; beneficial when filling a long column with this UDF
If rgx Is Nothing Then
Set rgx = CreateObject("VBScript.RegExp")
End If
metricMeasure = vbNullString
With rgx
.Global = True
.MultiLine = False
.Pattern = "[0-9]{1,4}m?[lL]t?r?"
If .Test(str) Then
Set cmat = .Execute(str)
'resize the nums array to accept the matches
ReDim nums(cmat.Count - 1)
'get measurement unit
unit = Replace(cmat.Item(0), Val(cmat.Item(0)), vbNullString, 1, vbTextCompare)
'populate the nums array with the matches
For n = LBound(nums) To UBound(nums)
nums(n) = Val(cmat.Item(n))
Next n
'convert the nums array to a subtotal with unit suffix
metricMeasure = Application.Sum(nums) & unit
End If
End With
End Function

Excel - Add sequential numbers to cell text based on line breaks

I have a worksheet that contains information on projects. The worksheet contains a column which contains risks for each project. There is a one-to-many relationship between a project and its risks.
Currently the risks for a projects are added to single cell and separated by a line break. I need to add sequential identifiers at the start of each risk. So for example inside a particular cell it should look like this. The sequential number should be bold if at all possible.
1).**Risk 1
2).**Risk 2
3).**Risk 3
etc.
Any suggestions on how to tackle this would be appreciated.
Here's how I'd approach it via UDF:
' Reformats a list from a simple delimitation to a numbered list
' Accepts arrays of strings for inList (allowing array formulas)
' numFormat is a standard Excel-style format string (default "0. ")
' inDelimiter is the delimiter in the input list
' outDelimiter is the delimiter for the output list
Public Function TO_NUMBERED_LIST(inList As Variant, Optional numFormat As Variant, _
Optional inDelimiter As Variant, Optional outDelimiter As Variant) As Variant
Dim i As Integer, j As Integer
' Set default parameters
If IsMissing(numFormat) Then numFormat = "0). "
If IsMissing(inDelimiter) Then inDelimiter = vbNewLine
If IsMissing(outDelimiter) Then outDelimiter = inDelimiter
If IsArray(inList) Then ' Must loop through each entry if using as an array formula
Dim outList() As Variant
ReDim outList(0 To (UBound(inList) - LBound(inList)), 1 To 1)
j = 0
For i = LBound(inList) To UBound(inList)
If IsError(inList(i, 1)) Then
outList(j, 1) = inList(i, 1)
Else
outList(j, 1) = MakeNumbered(CStr(inList(i, 1)), CStr(numFormat), CStr(inDelimiter), CStr(outDelimiter))
End If
j = j + 1
Next
TO_NUMBERED_LIST = outList
Else
TO_NUMBERED_LIST = MakeNumbered(CStr(inList), CStr(numFormat), CStr(inDelimiter), CStr(outDelimiter))
End If
End Function
' Helper function to do the actual work of splitting lists, numbering them, and recombining them
Private Function MakeNumbered(inList As String, Optional numFormat As String, _
Optional inDelimiter As String, Optional outDelimiter As String) As String
Dim i As Integer
Dim tokenArr() As String
tokenArr = Split(inList, inDelimiter)
For i = 0 To UBound(tokenArr)
tokenArr(i) = Format(i + 1, numFormat) & tokenArr(i)
Next
MakeNumbered = Join(tokenArr, outDelimiter)
End Function
I leverage some knowledge from your previous thread, like the fact that the input might be an array (and the whole function might be used in an array formula) but will only ever be 1-dimensional.
I've made this pretty general for reformatting. It can take in lists with any input delimiter (in your case, a newline) and output using any desired delimiter (in your case, still a newline). The numFormat parameter acts using the Format function and supports formats like you would commonly see in Excel. Check the documentation if you need help there.
Default parameters have already been tweaked for your example - newline as delimiter(s) and "0). " as numbering format.
You can use the Split function on each cell value to create an array of risks and then prefix each risk with the sequence id. Then you can use the Join function to put the array back into a single value to update the cell with.
Depending on how the newlines got into the cell you might need to use vbCrLf, or vbNewLine instead of vbLf in the following example code:
Option Explicit
Sub AddRiskSequence()
Dim rngRisks As Range
Dim rngCell As Range
Dim varRisks As Variant
Dim lngIndex As Long
'set range with risk values
Set rngRisks = Sheet2.Range("B2:B4")
'iterate cells in risk column
For Each rngCell In rngRisks
'split cell contents by line feed into array
varRisks = VBA.Split(rngCell.Value, vbLf)
'iterate array and add sequence ids
For lngIndex = 0 To UBound(varRisks)
varRisks(lngIndex) = VBA.CStr(lngIndex + 1) & ") " & varRisks(lngIndex)
Next lngIndex
'rejoin array and update cell value
rngCell.Value = VBA.Join(varRisks, vbLf)
Next rngCell
End Sub
Before:
After:

Custom sorting in VBA (possibly from a list)

Table to sort:
I have a 2000-ish entry table. The first column contains an ID (non-unique) of the following type: [numeric 1-52][letters][optional underscore][optional numeric 1-10]. Letters will be either [a], [b], [c], [sa], [sb], [sc].
Example: 1c, 10sb_3, 5a, 12c, 3sc, 21c_1, 22c_4, 22b_10, 14sb, 26sb.
How I want the sorting done
I want to sort by type (letter) first, in the order I named them before the example above. In case of same type, I want to sort by the first number. In case of same first number (both optional parameters will be present) I want to sort by the last number. The sorting should extend to the rest of the row (table) as well.
Desired end result
1c
1c
1c
2c
3c
3c
4c_1
4c_2
4c_3
5c
6c_1
.......
1b
2b
2b
3b
4b_1
4b_2
5b
5b
.......
etc
What I intended to do (may not be the best idea)
Using the answer of this question as a starting point: Code an Excel VBA sort with a custom order and a value containing commas
I could make an algorithm which creates a second list, on the side, removing all duplicates, and order that list how I want manually. It would take a while, and is possibly incredibly inefficient. When it is done, I would use a piece of code similar to the answer's:
Dim oWorksheet As Worksheet
Set oWorksheet = ActiveWorkbook.Worksheets("Sheet1")
Dim oRangeSort As Range
Dim oRangeKey As Range
' one range that includes all colums do sort
Set oRangeSort = oWorksheet.Range("A1:J2000") ' <<<<I'd set the range right, of course
' start of column with keys to sort
Set oRangeKey = oWorksheet.Range("B1") '<<<What is this for?
' custom sort order
Dim sCustomList(x To y) As String
'There would be a loop here filling the array in order with my manually sorted list
Application.AddCustomList ListArray:=sCustomList
' use this if you want a list on the spreadsheet to sort by
' Application.AddCustomList ListArray:=Range("D1:D3")
' ^^^^ for the record I'm not sure what this accomplishes in my case. Should I remove it? I feel it is just a different way to fill the array, using the range directly instead of filling with a loop. Maybe it suits me more?
oWorksheet.Sort.SortFields.Clear
oRangeSort.Sort Key1:=oRangeKey, Order1:=xlAscending, Header:=xlGuess, _
OrderCustom:=Application.CustomListCount + 1, MatchCase:=False, _
Orientation:=xlTopToBottom, DataOption1:=xlSortNormal
' clean up
Application.DeleteCustomList Application.CustomListCount
Set oWorksheet = Nothing
Personally, unless you need to do this as part of a larger piece of code, I wouldn't use VBA and would just do this by adding a column to your data sheet that gives you the correct sort order.
To extract the relevant pieces of your ID (assuming it starts in cell "A1") you need to pull out the letters from your string:
=MID(A1,MIN(FIND({"a","b","c","s"},A1&"abcs")),IF(ISNUMBER(FIND("s",A1)),2,1))
Next you need the first number:
=LEFT(A1,MIN(FIND({"a","b","c","s"},A1&"abcs"))-1)
Then you need to add on the second number if it exists:
=IF(ISNUMBER(FIND("_",A1)),RIGHT(A1,LEN(A1)-FIND("_",A1))*1,0)
Putting these all into a single formula and formatting the numbers to take account of single or double-digit numbers gives:
=MID(A1,MIN(FIND({"a","b","c","s"},A1&"abcs")),IF(ISNUMBER(FIND("s",A1)),2,1))&TEXT(LEFT(A1,MIN(FIND({"a","b","c","s"},A1&"abcs"))-1),"00")&TEXT(IF(ISNUMBER(FIND("_",A1)),RIGHT(A1,LEN(A1)-FIND("_",A1))*1,0),"00")
Which is probably not the simplest way of achieving it, but will give you a column of strings which you can use as your sort order.
The only thing I am confused about is that your question said that the letters needed to be sorted in the order that you listed them, but your example showed "c" coming before "b". If you need the letters to be in non-alphabetical order, we would need to adjust the first part of this formula.
If possible, to make it easier, the first thing I'd suggest is to keep the fields (type, first number, special parameter, optional numeric) all the same lenght, that would make the algorithm incredibly easier.
1c2 becomes 01oc-02
23sa_13 keeps 23sa_13
But if you don't have that possibility, here it goes:
This separates all values in a new sheet, one by one, including repeated
Option Explicit
Sub SortData()
Dim MySheet As Worksheet, NewSheet As Worksheet
Set MySheet = ThisWorkbook.Worksheets("Sheet1")
Set NewSheet = ThisWorkbook.Worksheets.Add()
NewSheet.Range("A1").value = "Type"
NewSheet.Range("B1").value = "First Number"
NewSheet.Range("C1").value = "Underscore"
NewSheet.Range("D1").value = "Last Number"
Dim CurrentRange As Range
Dim i As Integer
For i = 2 To 2000 'the rows you are going to consider
'you may replace this for a while cell is not empty check
'considering the first row is a header, not a value
Set CurrentRange = MySheet.Cells(i, 1) 'gets the cell in row i and column 1
Dim CurrentValue As String
CurrentValue = CurrentRange.value 'gets the value of the cell
'if cell is empty, stop loop
If CurrentValue = "" Then
Exit For
End If
Dim FirstNumberSize As Integer
Dim TypeSize As Integer
Dim UnderscoreSize As Integer
Dim LastNumberSize As Integer
Dim StartChar As Integer
StartChar = 1
Call GetFieldSizes(CurrentValue, FirstNumberSize, TypeSize, UnderscoreSize, LastNumberSize)
'write the values in a new sheet
NewSheet.Cells(i, 2).value = Mid(CurrentValue, StartChar, FirstNumberSize) 'write firstnumber in the new sheet
StartChar = StartChar + FirstNumberSize 'advance to the next field
NewSheet.Cells(i, 1).value = Mid(CurrentValue, StartChar, TypeSize) 'write type in the new sheet
StartChar = StartChar + TypeSize
NewSheet.Cells(i, 3).value = Mid(CurrentValue, StartChar, UnderscoreSize) 'write underscore in the new sheet - nothing if size is zero
StartChar = StartChar + UnderscoreSize
NewSheet.Cells(i, 4).value = Mid(CurrentValue, StartChar, LastNumberSize) 'write lastNumber in the new sheet - nothing if size is zero
Next
End Sub
Sub GetFieldSizes(value As String, ByRef firstNum As Integer, ByRef entryType As Integer, ByRef Underscore As Integer, ByRef lastNum As Integer)
'walk through each char of the value while it's a number
Dim Continue As Boolean
Dim charVal As String
Dim valLength As Integer
valLength = Len(value) 'the length of the string
'find first number size
firstNum = 0 'start from character zero
Continue = True 'to check if I can advance to the next char
Do
'if the next char is not a number, we found the letters
If Not IsNumeric(Mid(value, firstNum + 1, 1)) Then
Continue = False 'I say I cannot advance anymore, the size of our number is found
Else
firstNum = firstNum + 1 'advance one char
End If
Loop While Continue = True 'repeat while I can continue
'find first underscore or digit of last number
For Underscore = firstNum + 1 To valLength 'from the first char after the first number to the end of the string
charVal = Mid(value, Underscore, 1) 'get the value of the char in the current underscore position
If charVal = "_" Then 'if the char is an underscore
lastNum = valLength - Underscore 'the remaining chars are the last number
Underscore = 1 'the actual size of the underscore is 1
Exit For 'interrupt the loop
ElseIf IsNumeric(charVal) Then 'if the char is a number
lastNum = valLength - Underscore + 1 'the remaining chars, including this one are the last number
Underscore = 0 'if I find a number instead of the underscore, it doesn't exist, say it's length is zero
Exit For 'interrupt the loop
End If
Next
'if I advanced to the end of the string, I didn't find any number of underscore
If Underscore > valLength Then
lastNum = 0
Underscore = 0
End If
entryType = valLength - firstNum - Underscore - lastNum 'the size of the letters is the remaining size when you remove the other sizes
End Sub

Wildcard search of dictionary

After searching google and SO, I see that there is a way for me to search a dictionary for an existing key:
dict.exists("search string")
My question is how can I search a dictionary using a wildcard:
dict.exists("search*")
I want to search the dictionary for a term first because my macro has the user select a group of files (the file name as dictionary key and the full path as the value) and I want to determine if files of a certain naming convention are present in the group BEFORE I iterate the dictionary elements to apply the file processing.
If a certain naming convention is found, X processing is run on each file in the dictionary instead of Y processing. The trick is that if ANY of the elements follow the certain naming convention, then they all need to be processed accordingly. That is to say, if elements 1-19 fail to meet the convention but 20 passes, then all elements 1-20 need specific processing. This is the reason I cant just check each name as I go and process selectively one file at a time.
My current solution is to iterate the entire dictionary once searching for the naming convention, then reiterating the dictionary after I know which method to use in processing the files. I am looping through all the elements twice and that doesn't seem efficient...
Do you guys have a reasonable solution for wildcard searching the dictionary keys?
The Dictionary Items method returns an array of all the items. You can Join those into a big string then use Instr() to determine if your search string is in the big string.
From your example, you have the asterisk at the end, so I'm assuming you care how an item starts, not that a sub-string exists anywhere. So I look for delimiter+substring and add the delimiter to the front of the Join (for the sake of the first item). If you have different requirements, you'll have to adjust, but the theory is the same.
I used two pipes as a delimiter because it's unlikely to be in the data and return a false positive. That may not be appropriate for your data.
Public Function WildExists(ByRef dc As Scripting.Dictionary, ByVal sSearch As String) As Boolean
Const sDELIM As String = "||"
WildExists = InStr(1, sDELIM & Join(dc.Keys, sDELIM), sDELIM & sSearch) > 0
End Function
test code
Sub Test()
Dim dc As Scripting.Dictionary
Set dc = New Scripting.Dictionary
dc.Add "Apple", "Apple"
dc.Add "Banana", "Banana"
dc.Add "Pear", "Pear"
Debug.Print WildExists(dc, "App") 'true
Debug.Print WildExists(dc, "Ora") 'false
End Sub
You can use Filter combined with the array of dictionary keys to return an array of matching keys.
Function getMatchingKeys(DataDictionary As Dictionary, MatchString As String, Optional Include As Boolean = True, Optional Compare As VbCompareMethod = vbTextCompare) As String()
getMatchingKeys = Filter(DataDictionary.Keys, MatchString, Include, Compare)
End Function
Here are some examples of what can be done when you apply a filter to the dictionary's keys.
Option Explicit
Sub Examples()
Dim dict As Dictionary
Dim arrKeys() As String
Dim key
Set dict = New Dictionary
dict.Add "Red Delicious apples", 10
dict.Add "Golden Delicious Apples", 5
dict.Add "Granny Smith apples", 66
dict.Add "Gala Apples", 20
dict.Add "McIntosh Apples", 30
dict.Add "Apple Pie", 40
dict.Add "Apple Sauce", 50
dict.Add "Anjuo Pears", 60
dict.Add "Asian Pears", 22
dict.Add "Bartlett Pears", 33
dict.Add "Bosc Pears", 44
dict.Add "Comice Pears", 3
arrKeys = getMatchingKeys(dict, "Apple")
Debug.Print "Keys that contain Apple"
Debug.Print Join(arrKeys, ",")
Debug.Print
arrKeys = getMatchingKeys(dict, "Apple", False)
Debug.Print "Keys that do not contain Apple"
Debug.Print Join(arrKeys, ",")
Debug.Print
arrKeys = getMatchingKeys(DataDictionary:=dict, MatchString:="Apple", Include:=True, Compare:=vbBinaryCompare)
Debug.Print "Keys that contain matching case Apple"
Debug.Print Join(arrKeys, ",")
Debug.Print
arrKeys = getMatchingKeys(DataDictionary:=dict, MatchString:="Pears", Include:=True, Compare:=vbTextCompare)
Debug.Print "We can also use the array of keys to find the values in the dictionary"
Debug.Print "We have " & (UBound(arrKeys) + 1) & " types of Pears"
For Each key In arrKeys
Debug.Print "There are " & dict(key) & " " & key
Next
End Sub
Output:
this method can help you with wildcard searching in Dictionary
Sub test()
Dim Dic As Object: Set Dic = CreateObject("Scripting.Dictionary")
Dim KeY, i&: i = 1
For Each oCell In Range("A1:A10")
Dic.Add i, Cells(i, 1).Value: i = i + 1
Next
For Each KeY In Dic
If LCase(Dic(KeY)) Like LCase("Search*") Then
MsgBox "Wildcard exist!"
Exit For
End If
Next
End Sub
If you want to use a wildcard to search in dictionary keys you can use the method [yourdictionary].Keys and the function Application.Match
For example:
Dim position As Variant 'It will return the position for the first occurrence
position = Application.Match("*Gonzalez", phoneBook.Keys, 0)
If phoneBook has Keys: (JuanCarlos, LuisGonzalez, PedroGonzalez)
It will return the position for LuisGonzalez