在VBA中按键排序字典

11

我已经使用CreateObject("Scripting.Dictionary")在VBA中创建了一个字典,将源单词映射到要替换文本中的目标单词(实际上是用于混淆)。

不幸的是,当我按照下面的代码进行实际替换时,它会按照添加到字典中的顺序替换源单词。如果我有"Blue"和"Blue Berry",那么"Blue Berry"中的"Blue"部分将被第一个目标替换,而"Berry"保持不变。

'This is where I replace the values
For Each curKey In dctRepl.keys()
    largeTxt = Replace(largeTxt, curKey, dctRepl(curKey))
Next

我想通过首先按照最长的键长将字典的排序,然后像上面那样进行替换来解决这个问题。问题是我不知道如何以这种方式对键进行排序。


2
请查看cpearson.com - chris neilsen
1
@chrisneilsen:不错的链接,虽然不完全是我需要的。 - neelsg
6个回答

16

看起来我自己已经想出来了。 我创建了以下函数,似乎可以完成任务:

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

要了解静态数组的更多信息,请参阅:https://excelmacromastery.com/excel-vba-array/#Declaring_an_Array


1
@ashleedawg 感谢提供链接。看到其他人使用我的代码总是很开心。这就是SO的意义所在,我们都在前人的巨大工作基础上不断建设。 - neelsg
1
同意+...我希望在我开始学习的时候有一个"Stack Overflow"。那个年代基本上只有expertsexchange......很久以前他们加了连字符来结束有关他们名称的笑话。 - ashleedawg
我是否可以允许自己适应你的代码。通过编写以下内容,您可以消除一个循环:arrTemp = dicList.Keys - Oleksandr Krasnoshchok

6

我正在寻找一个简单的VBA函数,在Microsoft Excel中按键值升序排序字典。

我对neelsg的代码进行了一些小修改以适应我的需求(请参见下面的'//注释以获取修改详情):

'/* Wrapper (accurate function name) */
Public Function funcSortDictByKeyAscending(dctList As Object) As Object
    Set funcSortDictByKeyAscending = funcSortKeysByLengthDesc(dctList)
End Function

'/* neelsg's code (modified) */
Public Function funcSortKeysByLengthDesc(dctList As Object) As Object
'//    Dim arrTemp() As String
    Dim arrTemp() As Variant
...
...
...
        '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
                If arrTemp(itX) > arrTemp(itY) Then
...
...
...
        'Create the new dictionary
'//        Set funcSortKeysByLengthDesc = CreateObject("Scripting.Dictionary")
        Set d = CreateObject("Scripting.Dictionary")
        For itX = 0 To (dctList.Count - 1)
'//            funcSortKeysByLengthDesc.Add arrTemp(itX), dctList(arrTemp(itX))
            d(arrTemp(itX)) = dctList(arrTemp(itX))
        Next
'// Added:
        Set funcSortKeysByLengthDesc = d
    Else
        Set funcSortKeysByLengthDesc = dctList
    End If
End Function

4
最好使用完整干净的代码来阅读,而不是注释掉的“差异”版本。 - Askolein

2
另一种可能是使用ArrayList对字典键进行排序,然后使用ArrayList的值来重新创建字典。
  Private Sub SortDictionary(oDictionary As Scripting.Dictionary)
  On Error Resume Next
  Dim oArrayList As Object
  Dim oNewDictionary As Scripting.Dictionary
  Dim vKeys As Variant, vKey As Variant
     Set oArrayList = CreateObject("System.Collections.ArrayList")

     ' Transpose Keys into ones based array.
     vKeys = oDictionary.Keys
     vKeys = Application.WorksheetFunction.Transpose(vKeys)
     For Each vKey In vKeys
         Call oArrayList.Add(vKey)
     Next
     oArrayList.Sort
     ''oArrayList.Reverse
   
     ' Create a new dictionary with the same characteristics as the old dictionary.
     Set oNewDictionary = New Scripting.Dictionary
     oNewDictionary.CompareMode = oDictionary.CompareMode

     ' Iterate over the array list and transfer values from old dictionary to new dictionary.
     For Each vKey In oArrayList
         sKey = CStr(vKey)
         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 oArrayList = Nothing
 On Error GoTo 0
 End Sub

不确定这是否仍然相关,但我发现以下帖子:“在VBA中使用System.Collections.ArrayList - 需要哪个.NET Framework版本?”位于https://dev59.com/Qj8EtIcB2Jgan1znDDUO。该主题的要点是,无论您安装了哪个.NET Framework版本,您都必须安装.NET Framework 3.5才能在Excel VBA中使用ArrayList。 - j2associates
这是另一个。免责声明,这可能是一些过时的资料,因为它涉及到 .Net Framework 2.0。我不再做很多 VBA,所以没有简单的方法来测试这个。如何从 Excel VBA 调用 .NET 方法?请参阅 https://dev59.com/jFoU5IYBdhLWcg3wzZPw?rq=1 - j2associates
在Windows 10下的Excel 2016中,我使用良好。 - Paul Russell

1

可以通过在过程中添加代码而不创建函数来对键进行排序。

Dim sdkey,tamp As Variant
With CreateObject("Scripting.Dictionary")
sdkey = .keys
For i = LBound(sdkey) + 1 To UBound(sdkey)    ' We've listed the unique values as alphabetically.
For j = LBound(sdkey) To UBound(sdkey) - 1
If sdkey(j) > sdkey(i) Then
tamp = sdkey(j)
sdkey(j) = sdkey(i)
sdkey(i) = tamp
End If
Next j
Next i

ReDim tamp(1 To UBound(sd_key) + 1, 1 To 2)
…

enter image description here

示例文件来源:在Excel中获取不同值的总和


1
我知道这是一个旧的主题,但当我遇到类似的问题时它很有帮助。我的解决方案是使用沙盒工作表,让Excel对键进行排序,然后重新构建字典。通过使用沙盒工作表,您可以非常容易地使用公式来处理其他困难的排序情况,而无需编写自己的冒泡排序。对于原始帖子的情况,按Len(Key)降序排序将解决问题。
这是我的代码:
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

0
现在你可以获取一个排序后的键的数组,并使用它来重新创建字典,或者按顺序遍历它而无需重新创建,方法如下:
arrSortedKeys = Application.WorksheetFunction.Sort(Application.WorksheetFunction.Transpose(myDictionary.Keys))

你可以通过使用by_col参数来避免转置。arrSortedKeys = Application.WorksheetFunction.Sort(myDictionary.Keys,,,TRUE) - undefined
这是更好的。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接