I have created a dictionary in VBA using CreateObject("Scripting.Dictionary")
that maps source words to target words to be replaced in some text (This is actually for obfuscation).
Unfortunately, when I do the actual replace as per the code below, it will replace the source words in the order they were added to the dictionary. If I then have for instance "Blue" and then "Blue Berry", the "Blue" part in "Blue Berry" is replaced by the first target and " Berry" remains as it was.
'This is where I replace the values
For Each curKey In dctRepl.keys()
largeTxt = Replace(largeTxt, curKey, dctRepl(curKey))
Next
I'm thinking that I could resolve this issue by first sorting the dictionary's keys from longest length to shortest length and then doing the replace as above. The problem is I don't know how to sort the keys this way.
I know this is an old thread but it was helpful when I encountered a similar issue. My solution was to use a Sandbox worksheet and let Excel sort the keys and then just rebuild the dictionary. By using a Sandbox worksheet, you can very easily use formulas for otherwise difficult sorting situations without having to write your own bubble sort on the keys. In the case of the original poster, sorting descending on Len(Key) would have solved the problem.
Here is my code:
Private Sub SortDictionary(oDictionary As Scripting.Dictionary, oSandboxSheet As Worksheet)
On Error Resume Next
Dim oSortRange As Range
Dim oNewDictionary As Scripting.Dictionary
Dim lBegRow As Long, lEndRow As Long, lBegCol As Long, lEndCol As Long
Dim lIndex As Long
Dim sKey As String
Dim vKeys As Variant
' Transpose Keys into ones based array.
vKeys = oDictionary.Keys
vKeys = Application.WorksheetFunction.Transpose(vKeys)
' Calculate sheet rows and columns based upon array dimensions.
lBegRow = LBound(vKeys, 1): lEndRow = UBound(vKeys, 1)
lBegCol = LBound(vKeys, 2): lEndCol = UBound(vKeys, 2)
With oSandboxSheet
.Activate
.Cells.EntireColumn.Clear
' Copy the keys to Excel Range calculated from Keys array dimensions.
.Range(.Cells(lBegRow, lBegCol), .Cells(lEndRow, lEndCol)).Value = vKeys
.Cells.EntireColumn.AutoFit
' Sort the entire range.
Set oSortRange = .Range(.Cells(lBegRow, lBegCol), .Cells(lEndRow, lEndCol))
With .Sort
With .SortFields
.Clear
Call .Add(Key:=oSortRange, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal)
End With
Call .SetRange(oSortRange)
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
' Recreate the keys now sorted as desired.
vKeys = .Range(.Cells(lBegRow, lBegCol), .Cells(lEndRow, lEndCol)).Value
End With
' Create a new dictionary with the same characteristics as the old dictionary.
Set oNewDictionary = New Scripting.Dictionary
oNewDictionary.CompareMode = oDictionary.CompareMode
' Iterate over the new sorted keys and transfer values from old dictionary to new dictionary.
For lIndex = LBound(vKeys, 1) To UBound(vKeys, 1)
sKey = vKeys(lIndex, 1)
If oDictionary.Exists(sKey) Then
Call oNewDictionary.Add(sKey, oDictionary.Item(sKey))
End If
Next
' Replace the old dictionary with new sorted dictionary.
Set oDictionary = oNewDictionary
Set oNewDictionary = Nothing: Set oSortRange = Nothing
On Error GoTo 0
End Sub
It looks like I figured it out myself. I created the following function that appears to be doing the job:
Public Function funcSortKeysByLengthDesc(dctList As Object) As Object
Dim arrTemp() As String
Dim curKey As Variant
Dim itX As Integer
Dim itY As Integer
'Only sort if more than one item in the dict
If dctList.Count > 1 Then
'Populate the array
ReDim arrTemp(dctList.Count - 1)
itX = 0
For Each curKey In dctList
arrTemp(itX) = curKey
itX = itX + 1
Next
'Do the sort in the array
For itX = 0 To (dctList.Count - 2)
For itY = (itX + 1) To (dctList.Count - 1)
If Len(arrTemp(itX)) < Len(arrTemp(itY)) Then
curKey = arrTemp(itY)
arrTemp(itY) = arrTemp(itX)
arrTemp(itX) = curKey
End If
Next
Next
'Create the new dictionary
Set funcSortKeysByLengthDesc = CreateObject("Scripting.Dictionary")
For itX = 0 To (dctList.Count - 1)
funcSortKeysByLengthDesc.Add arrTemp(itX), dctList(arrTemp(itX))
Next
Else
Set funcSortKeysByLengthDesc = dctList
End If
End Function
For more info on static arrays see: https://excelmacromastery.com/excel-vba-array/#Declaring_an_Array
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With