使用 Excel 中的 VBA 将项目添加到字典中的特定位置

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10789534/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-11 16:19:00  来源:igfitidea点击:

Add Item to Specific Location in Dictionary with VBA in Excel

excelvbadictionary

提问by Ehudz

I need to add an item after a specific key and item pair in a dictionary. Essentially the same behavior which the add member allows for in a collection: (Collection.Add (item [,key] [,before] [,after])

我需要在字典中的特定键和项目对之后添加一个项目。本质上与 add 成员在集合中允许的行为相同:(Collection.Add (item [,key] [,before] [,after])

回答by mwolfe02

There is no built-in method of the Dictionary object that allows this. Here is a quick way to roll your own. This will accomplish specifically what you are asking for but it should be simple to modify:

没有允许这样做的 Dictionary 对象的内置方法。这是一种快速滚动的方法。这将具体完成您的要求,但修改起来应该很简单:

Function DictAdd(StartingDict As Dictionary, Key, Item, AfterKey) As Dictionary
Dim DictKey As Variant

    Set DictAdd = New Dictionary
    For Each DictKey In StartingDict
        DictAdd.Add DictKey, StartingDict(DictKey)
        If DictKey = AfterKey Then DictAdd.Add Key, Item
    Next DictKey
End Function

And to test it run the following procedure:

要测试它,请运行以下程序:

Sub TestDictAdd()
Dim MyDict As New Dictionary, DictKey As Variant

    MyDict.Add "A", "Alpha"
    MyDict.Add "C", "Charlie"

    Set MyDict = DictAdd(MyDict, "B", "Bravo", "A")
    For Each DictKey In MyDict
        Debug.Print DictKey, MyDict(DictKey)
    Next DictKey
End Sub

This is just to get you started. If I were doing this for myself I would probably create my own custom classto use and create a custom Add methodinstead of using a function. I'd also make the following improvements:

这只是让你开始。如果我为自己这样做,我可能会创建自己的自定义类来使用并创建自定义 Add 方法而不是使用函数。我还将进行以下改进:

  • add error handling
  • make AfterKey an optional parameter
  • add BeforeKey as an optional parameter
  • 添加错误处理
  • 使 AfterKey 成为可选参数
  • 添加 BeforeKey 作为可选参数

回答by Siddharth Rout

Like this?

像这样?

Option Explicit

Sub Sample()
    Dim Dict As Dictionary
    Dim itm As Variant

    Set Dict = New Dictionary

    Dict.Add "MyKey1", "Hello"
    Dict.Add "MyKey2", "This"
    Dict.Add "MyKey3", "is"
    Dict.Add "MyKey4", "Example"

    '~~> USAGE: Dictionaty Object, Key, Text, Position        
    Additem Dict, "MyKey5", "An", 3

    For Each itm In Dict
        Debug.Print itm & " - " & Dict(itm)
    Next
End Sub

Function Additem(ByRef D As Dictionary, ky As Variant, itm As Variant, pos As Long)
    Dim kyAr() As Variant, itmAr() As Variant
    Dim temp1() As Variant, temp2() As Variant
    Dim i As Long

    kyAr = D.Keys: itmAr = D.Items

    ReDim temp1(UBound(kyAr) + 1)
    ReDim temp2(UBound(itmAr) + 1)

    For i = 0 To pos - 1
        temp1(i) = kyAr(i): temp2(i) = itmAr(i)
    Next

    temp1(pos) = ky: temp2(pos) = itm

    For i = pos + 1 To UBound(temp1)
        temp1(i) = kyAr(i - 1): temp2(i) = itmAr(i - 1)
    Next

    ReDim kyAr(0): ReDim itmAr(0)

    kyAr() = temp1(): itmAr() = temp2()

    D.RemoveAll

    For i = LBound(kyAr) To UBound(kyAr)
        D.Add kyAr(i), itmAr(i)
    Next i
End Function

OUTPUT

输出

BEFORE

MyKey1 - Hello
MyKey2 - This
MyKey3 - is
MyKey4 - Example

AFTER

MyKey1 - Hello
MyKey2 - This
MyKey3 - is
MyKey5 - An
MyKey4 - Example

回答by Walter Rauschenberger

Instead of sorting the dictionary when it contains all items I implemented a little procedure called DctAdd which keeps the keys sorted immediately when adding an item. Assuming the key is vAdd, the item is vItem, both of type variant and the Dictionary to have sorted is dct. So instead of :

当字典包含所有项目时,我没有对字典进行排序,而是实现了一个名为 DctAdd 的小程序,它在添加项目时立即对键进行排序。假设key是vAdd,item是vItem,类型variant和要排序的Dictionary都是dct。所以而不是:

dct.Add vAdd, vItem

I use:

我用:

DctAdd dct, vItem, vAdd, dam_sortasc 

For the performance I included only some basic test since I found it sufficient when using it in my project.

对于性能,我只包含了一些基本测试,因为我发现在我的项目中使用它时已经足够了。

To use DctAdd the following has to be copied to the declaration section of the concerned module:

要使用 DctAdd,必须将以下内容复制到相关模块的声明部分:

' Just for the performance time measurement -----------------------------
Private Declare Function GetTime Lib "winmm.dll" Alias "timeGetTime" () As Long
' For the execution mode of DctAdd --------------------------------------
' (may be extended to also cover insert before and after)
Public Enum enAddInsertMode
    dam_sortasc = 1
    dam_sortdesc = 2
End Enum

The following code can be copied to any standard or class module: Please note that insert before/after has yet not been implemented but should not take very long to add.

以下代码可以复制到任何标准或类模块中: 请注意,insert before/after 尚未实现,但不应花很长时间添加。

Public Sub DctAdd(ByRef dct As Scripting.Dictionary, _
                  ByVal vItem As Variant, _
                  ByVal vAdd As Variant, _
                  ByVal lMode As enAddInsertMode)
' ----------------------------------------------------------------------
' Add to the Dictionary dct the item vItem with vAdd as the key,
' sorted in ascending or descending order.
'
' If the vAdd key already exists, adding it will be skipped without
' an error. A not existing dictionary is established with the first add
'
' W. Rauschenberger, [email protected], Berlin, Feb 2015
' ----------------------------------------------------------------------
Dim i           As Long
Dim dctTemp     As Scripting.Dictionary
Dim vTempKey    As Variant
Dim bAdd        As Boolean

    If dct Is Nothing Then Set dct = New Dictionary

    With dct
        If .count = 0 Then
            .Add vAdd, vItem
            Exit Sub
        Else
            ' -----------------------------------------------------------
            ' The can maybee added directly after the last key
            ' -----------------------------------------------------------
            vTempKey = .Keys()(.count - 1)      ' Get the very last key
            Select Case lMode
                Case dam_sortasc
                    If vAdd > vTempKey Then
                        .Add vAdd, vItem
                        Exit Sub                ' Done!
                    End If
                Case dam_sortdesc
                    If vAdd < vTempKey Then
                        .Add vAdd, vItem
                        Exit Sub                ' Done!
                    End If
            End Select
        End If
    End With

    ' -----------------------------------------------------------------
    ' Since the new key could not simply be added to the dct it must be
    ' added/inserted somewhere in between or before the very first key
    ' ------------------------------------------------------------------
    Set dctTemp = New Dictionary
    bAdd = True
    For Each vTempKey In dct
        With dctTemp
            If bAdd Then ' When the new item has yet not been added
                Select Case lMode
                    Case dam_sortasc
                        If vTempKey > vAdd Then
                            If Not dct.Exists(vAdd) Then
                               .Add vAdd, vItem
                            End If
                            bAdd = False ' Add done
                        End If
                    Case dam_sortdesc
                        If vTempKey < vAdd Then
                            If Not dct.Exists(vAdd) Then
                                .Add vAdd, vItem
                            End If
                            bAdd = False ' Add done
                        End If
                End Select
            End If
            .Add vTempKey, dct.Item(vTempKey)
        End With
    Next vTempKey
                               ' ------------------------------------
    Set dct = dctTemp          ' Return the temporary dictionary with
    Set dctTemp = Nothing      ' the added new item
    Exit Sub                   ' ------------------------------------

on_error:
   Debug.Print "Error in 'DctAdd'!"
End Sub

And this I used for testing:

这是我用来测试的:

Public Sub Testdct1Add()
Dim dct1    As Scripting.Dictionary
Dim dct2    As Scripting.Dictionary
Dim i       As Long
Dim lStart  As Long
Dim lAdd    As Long
Dim vKey    As Variant


    ' -----------------------------------------------------------------------
    Debug.Print vbLf & "DctAdd: Test ascending order"
    ' Add sorted ascending with the key provided in the reverse order
    Set dct1 = Nothing
    For i = 10 To 1 Step -1
        DctAdd dct1, i, i, dam_sortasc
    Next i
    ' Show the result and wait ----------------
    For Each vKey In dct1
        Debug.Print vKey & " " & dct1.Item(vKey)
    Next vKey
    Stop

    ' ------------------------------------------------------------------
    Debug.Print vbLf & "DctAdd: Test descending order"
    ' Add sorted ascending with the key provided in the reverse order
    Set dct1 = Nothing
    For i = 1 To 10
        DctAdd dct1, i, i, dam_sortdesc
    Next i
    ' Show the result and wait ----------------
    For Each vKey In dct1
        Debug.Print vKey & " " & dct1.Item(vKey)
    Next vKey
    Stop

    ' ------------------------------------------------------------------
    lAdd = 500
    Debug.Print vbLf & "DctAdd: Test a best case scenario by adding " & _
                vbLf & lAdd & " items in the desired sort order"
    Set dct1 = Nothing
    lStart = GetTime
    For i = 1 To lAdd
        DctAdd dct1, i, i, dam_sortasc
    Next i
    Debug.Print "Adding " & dct1.count & " items in the target " & _
         vbLf & "sort order = " & GetTime - lStart & " ms"
    Stop

    ' ------------------------------------------------------------------
    lAdd = 500
    Debug.Print vbLf & "DctAdd: Worst case scenarion test by adding " & _
                vbLf & lAdd & " items in the reverse sort order"
    Set dct1 = Nothing
    lStart = GetTime
    For i = lAdd To 1 Step -1
        DctAdd dct1, i, i, dam_sortasc
    Next i
    Debug.Print "Adding " & dct1.count & " items, 4 out of " & vbLf & _ 
                "order = " & GetTime - lStart & " ms"
    Stop

    ' -----------------------------------------------------------------
    lAdd = 1000
    Debug.Print vbLf & "DctAdd: Worst case scenarion test by adding " & _
                vbLf & lAdd & " items in the reverse sort order"
    Set dct1 = Nothing
    lStart = GetTime
    For i = lAdd To 1 Step -1
        DctAdd dct1, i, i, dam_sortasc
    Next i
    Debug.Print "Adding " & dct1.count & " items:" & vbLf & _
                GetTime - lStart & " ms"

    Stop

    ' -----------------------------------------------------------------
    ' Example for using dctAdd to sort any dictionary. The item if dct2 
    ' are temporarily added sorted ascending to the dct1 and finally set 
    ' to dct2
    ' ------------------------------------------------------------------
    Debug.Print vbLf & "DctAdd: Used to sort another Dictionary (dct2)"
    Set dct2 = New Dictionary
    dct2.Add "F", 1
    dct2.Add "A", 2
    dct2.Add "C", 3
    dct2.Add "H", 4
    dct2.Add "D", 5
    dct2.Add "E", 6
    dct2.Add "G", 7
    dct2.Add "B", 8

    Set dct1 = Nothing
    For Each vKey In dct2
        DctAdd dct1, dct2(vKey), vKey, dam_sortasc
    Next vKey
    Set dct2 = dct1
    ' Show the result and wait ----------------
    For Each vKey In dct2
        Debug.Print "Key=" & vKey & ", Item=" & dct2.Item(vKey)
    Next vKey

End Sub