I like @JvdV answer and it's probably the simplest to implement while also being independent of the application.
My answer will focus on improving the speed by using the Like
operator and I will provide a comparison with the other answers.
The idea is to iterate all keys with something like:
For Each tkey In Dict
If tkey Like "*apple*" Then
...
End If
Next tkey
We will need a support function to adapt patterns if we need case-insensitive comparisons. For example If tkey Like "*apple*" Then
is case-sensitive and we need If tkey Like "*[Aa][Pp][Pp][Ll][Ee]*" Then
for case-insensitive comparisons.
To adapt patterns, we need the following support function:
Public Function CaseInsensitivePattern(ByRef pattern As String) As String
Dim chars As Long: chars = Len(pattern)
Dim i As Long
Dim j As Long
Dim chL As String
Dim chU As String
'
If chars = 0 Then Exit Function
CaseInsensitivePattern = Space$(chars * 4) 'Init Buffer
'
j = 1
For i = 1 To chars
chL = LCase$(Mid$(pattern, i, 1))
chU = UCase$(chL)
If chL = chU Then
Mid$(CaseInsensitivePattern, j) = chL
j = j + 1
Else
Mid$(CaseInsensitivePattern, j) = "[" & chU & chL & "]"
j = j + 4
End If
Next i
CaseInsensitivePattern = Left$(CaseInsensitivePattern, j - 1)
End Function
In order to test this on lots of items, I will use VBA-FastDictionary instead of Scripting.Dictionary. Adding 1 million text keys with Scripting.Dictionary takes about 23 seconds on my computer while Fast Dictionary only takes about 0.65 seconds. There is a benchmarking section if you want more details. If you don't mind waiting to add the items then continue to use Scripting.Dictionary.
Here is the comparison:
Sub TestSpeed()
Dim i As Long
Const iterations As Long = 1000000
Dim t As Double
Dim dict As New Dictionary
Dim v As Variant
Dim coll As Collection
'
'Add key-item pairs
For i = 1 To iterations
dict.Add "Key " & i, i
If i Mod iterations \ 10 = 0 Then dict.Add "apple " & i, i
Next i
Debug.Print "Results in seconds"
Debug.Print "------------------"
'
'Filter solution
Dim arr As Variant
t = Timer
arr = Filter(dict.Keys, "test", True, vbTextCompare)
Debug.Print "Filter on Dict.Keys: " & Round(Timer - t, 3)
'
'Match solution
Dim vMatch As Variant
t = Timer
vMatch = Application.Match("*apple*", dict.Keys, 0)
Debug.Print "Match on Dict.Keys: " & Round(Timer - t, 3)
'
'Pattern match case-sensitive
Dim pattern As String: pattern = "*apple*"
Set coll = New Collection
t = Timer
For Each v In dict
If v Like pattern Then coll.Add v
Next v
Debug.Print "Pattern match case-sensitive on Dict (implicit keys): " & Round(Timer - t, 3)
'
'Pattern match case-insensitive
Dim patternI As String: patternI = CaseInsensitivePattern(pattern)
Set coll = New Collection
t = Timer
For Each v In dict
If v Like patternI Then coll.Add v
Next v
Debug.Print "Pattern match case-insensitive on Dict (implicit keys): " & Round(Timer - t, 3)
'
'Pattern match case-sensitive on keys array
Set coll = New Collection
t = Timer
For Each v In dict.Keys
If v Like pattern Then coll.Add v
Next v
Debug.Print "Pattern match case-sensitive on Dict.Keys: " & Round(Timer - t, 3)
End Sub
On my VBA7 x64 Win computer I get the following results in the Immediate window:
Results in seconds
------------------
Filter on Dict.Keys: 0.309
Match on Dict.Keys: 0.133
Pattern match case-sensitive on Dict (implicit keys): 0.074
Pattern match case-insensitive on Dict (implicit keys): 0.133
Pattern match case-sensitive on Dict.Keys: 0.098
Note that the Match
approach only returns the first found result while all others return all matches in either an array or a collection result.