VBA有词典结构吗?

257 vb6 vba dictionary data-structures

VBA有字典结构吗?像键<>值数组?

Mit*_*eat 332

是.

设置对MS Scripting运行时的引用('Microsoft Scripting Runtime').根据@ regjo的评论,转到Tools-> References并勾选"Microsoft Scripting Runtime"框.

参考窗口

使用以下代码创建字典实例:

Set dict = CreateObject("Scripting.Dictionary")
Run Code Online (Sandbox Code Playgroud)

要么

Dim dict As New Scripting.Dictionary 
Run Code Online (Sandbox Code Playgroud)

使用示例:

If Not dict.Exists(key) Then 
    dict.Add key, value
End If 
Run Code Online (Sandbox Code Playgroud)

Nothing完成使用后,不要忘记将字典设置为.

Set dict = Nothing 
Run Code Online (Sandbox Code Playgroud)

  • 只是为了完整起见:您需要引用"Microsoft Scripting Runtime"才能使用(转到Tools-> References)并选中它的框. (162认同)
  • 此数据结构类型由脚本运行时提供,而不是由VBA提供.基本上,VBA几乎可以使用任何可通过COM接口访问的数据结构类型. (17认同)
  • 我正在使用Excel 2010 ...但没有引用"Microsoft Scripting Runtime"工具 - 参考..只是做CreateObject不起作用.那么,@ masterjo我认为你上面的评论是错误的.除非我遗漏了什么..所以,伙计工具 - >引用是必需的. (8认同)
  • 呃,VBA系列是关键的.但也许我们对"键控"有不同的定义. (6认同)
  • 作为一个FYI,你不能使用`Dim dict As New Scripting.Dictionary`而没有参考.如果没有引用,则必须使用后期绑定`CreateObject`方法来实例化此对象. (4认同)
  • @ David-W-Fenton:收藏品没有键入 (3认同)
  • 在Excel 2010中,使用CreateObject()方法时不需要引用"Microsoft Scripting Runtime". (3认同)
  • 要创建对"Microsoft Scripting Runtime"的引用,您还可以通过添加以下代码来执行此操作:`ThisWorkbook.VBProject.References.AddFromFile"C:\ windows\system32\scrrun.dll"` (3认同)
  • 有人可以向我解释一下Scripting Runtime的Dictionary对象与VBA集合有何不同? (2认同)
  • @MitchWheat 您也可以编写`dict(key) = value`,而无需明确检查键是否存在。 (2认同)

Cal*_*ngh 172

VBA有集合对象:

    Dim c As Collection
    Set c = New Collection
    c.Add "Data1", "Key1"
    c.Add "Data2", "Key2"
    c.Add "Data3", "Key3"
    'Insert data via key into cell A1
    Range("A1").Value = c.Item("Key2")
Run Code Online (Sandbox Code Playgroud)

Collection对象使用哈希执行基于密钥的查找,因此它很快.


您可以使用Contains()函数检查特定集合是否包含密钥:

Public Function Contains(col As Collection, key As Variant) As Boolean
    On Error Resume Next
    col(key) ' Just try it. If it fails, Err.Number will be nonzero.
    Contains = (Err.Number = 0)
    Err.Clear
End Function
Run Code Online (Sandbox Code Playgroud)

编辑2015年6月24日:Contains()感谢@TWiStErRob.

编辑2015年9月25日:Err.Clear()感谢@scipilot.

  • 关于集合对象的坏处是,您无法检查集合中是否已存在密钥.它只会抛出一个错误.这是件大事,我不喜欢收藏品.(我知道,有一些解决方法,但大多数都是"丑陋的") (8认同)
  • 为指出内置的Collection对象做得好可以用作字典,因为Add方法有一个可选的"key"参数. (5认同)
  • 请注意,VBA字典中的字符串键(例如,c.Item("Key2"))的查找是散列的,但是通过整数索引(例如,c.Item(20))查找不是 - 它是/ next的线性风格搜索,应该避免.最好只使用集合来进行字符串键查找或每次迭代. (5认同)
  • 也许该函数应命名为`ContainsKey`; 只读取调用的人可能会混淆它以检查它是否包含特定值. (5认同)
  • 我找到了一个较短的[`Contains`](http://codevba.com/help/collection.htm#.VYVZAjCqopQ):`On Error Resume Next```col(key)`_`Contains =(Err.Number = 0)` (4认同)

Jar*_*rmo 43

VBA没有字典的内部实现,但是从VBA仍然可以使用MS Scripting Runtime Library中的字典对象.

Dim d
Set d = CreateObject("Scripting.Dictionary")
d.Add "a", "aaa"
d.Add "b", "bbb"
d.Add "c", "ccc"

If d.Exists("c") Then
    MsgBox d("c")
End If
Run Code Online (Sandbox Code Playgroud)


Joh*_*n M 29

一个附加的字典示例,可用于包含出现的频率.

循环之外:

Dim dict As New Scripting.dictionary
Dim MyVar as String
Run Code Online (Sandbox Code Playgroud)

在一个循环中:

'dictionary
If dict.Exists(MyVar) Then
    dict.Item(MyVar) = dict.Item(MyVar) + 1 'increment
Else
    dict.Item(MyVar) = 1 'set as 1st occurence
End If
Run Code Online (Sandbox Code Playgroud)

检查频率:

Dim i As Integer
For i = 0 To dict.Count - 1 ' lower index 0 (instead of 1)
    Debug.Print dict.Items(i) & " " & dict.Keys(i)
Next i
Run Code Online (Sandbox Code Playgroud)


Eva*_*edy 9

建立关cjrh的回答中,我们可以构建一个包含不需要标签(我不喜欢使用标签)功能.

Public Function Contains(Col As Collection, Key As String) As Boolean
    Contains = True
    On Error Resume Next
        err.Clear
        Col (Key)
        If err.Number <> 0 Then
            Contains = False
            err.Clear
        End If
    On Error GoTo 0
End Function
Run Code Online (Sandbox Code Playgroud)

对于我的一个项目,我编写了一组辅助函数来使Collection行为更像是一个Dictionary.它仍然允许递归集合.您会注意到Key始终是第一位的,因为它是强制性的,在我的实现中更有意义.我也只使用了String钥匙.如果你愿意,你可以改回来.

我将其重命名为set,因为它将覆盖旧值.

Private Sub cSet(ByRef Col As Collection, Key As String, Item As Variant)
    If (cHas(Col, Key)) Then Col.Remove Key
    Col.Add Array(Key, Item), Key
End Sub
Run Code Online (Sandbox Code Playgroud)

得到

这些err东西是用于对象的,因为你可以使用set和没有传递对象.我想你可以检查一下它是否是一个物体,但是时间紧迫.

Private Function cGet(ByRef Col As Collection, Key As String) As Variant
    If Not cHas(Col, Key) Then Exit Function
    On Error Resume Next
        err.Clear
        Set cGet = Col(Key)(1)
        If err.Number = 13 Then
            err.Clear
            cGet = Col(Key)(1)
        End If
    On Error GoTo 0
    If err.Number <> 0 Then Call err.raise(err.Number, err.Source, err.Description, err.HelpFile, err.HelpContext)
End Function
Run Code Online (Sandbox Code Playgroud)

具有

这篇帖子的原因......

Public Function cHas(Col As Collection, Key As String) As Boolean
    cHas = True
    On Error Resume Next
        err.Clear
        Col (Key)
        If err.Number <> 0 Then
            cHas = False
            err.Clear
        End If
    On Error GoTo 0
End Function
Run Code Online (Sandbox Code Playgroud)

去掉

如果它不存在则不抛出.只是确保它被删除.

Private Sub cRemove(ByRef Col As Collection, Key As String)
    If cHas(Col, Key) Then Col.Remove Key
End Sub
Run Code Online (Sandbox Code Playgroud)

按键

获取一系列密钥.

Private Function cKeys(ByRef Col As Collection) As String()
    Dim Initialized As Boolean
    Dim Keys() As String

    For Each Item In Col
        If Not Initialized Then
            ReDim Preserve Keys(0)
            Keys(UBound(Keys)) = Item(0)
            Initialized = True
        Else
            ReDim Preserve Keys(UBound(Keys) + 1)
            Keys(UBound(Keys)) = Item(0)
        End If
    Next Item

    cKeys = Keys
End Function
Run Code Online (Sandbox Code Playgroud)


Mat*_*hen 6

是.适用于VB6,VBA(Excel)和VB.NET

  • 我承认.我读得太快了.但我确实告诉了他需要知道的事情. (5认同)
  • @Oorang,绝对没有证据表明VBA成为VB.NET的一个子集,Office中的反向计算规则 - 想象一下试图转换所有写入的Excel宏. (4认同)
  • 您可以阅读更多问题:我已经询问过VBA:Visual Basic for Application,不是VB,不适用于VB.Net,不适用于任何其他语言. (2认同)
  • VBA实际上是VB6的SUPERSET.它使用与VB6相同的核心DLL,但随后为Office中的特定应用程序添加了各种功能. (2认同)

小智 6

脚本运行时字典似乎有一个可能在高级阶段破坏您的设计的错误.

如果字典值是数组,则无法通过对字典的引用来更新数组中包含的元素的值.


小智 5

如果出于某种原因,您无法或不想在 Excel 中安装其他功能,您也可以使用数组,至少对于简单的问题是这样。作为 WhatIsCapital,您输入国家/地区名称,该函数将返回其首都。

Sub arrays()
Dim WhatIsCapital As String, Country As Array, Capital As Array, Answer As String

WhatIsCapital = "Sweden"

Country = Array("UK", "Sweden", "Germany", "France")
Capital = Array("London", "Stockholm", "Berlin", "Paris")

For i = 0 To 10
    If WhatIsCapital = Country(i) Then Answer = Capital(i)
Next i

Debug.Print Answer

End Sub
Run Code Online (Sandbox Code Playgroud)

  • 这个答案的概念是合理的,但示例代码不会按编写的方式运行。每个变量都需要自己的“Dim”关键字,由于使用“Array()”,需要将“Country”和“Capital”声明为 Variants,应该声明“i”(如果“Option Explicit”则必须声明)已设置),并且循环计数器将抛出越界错误——使用“UBound(Country)”作为“To”值更安全。另外也许值得注意的是,虽然“Array()”函数是一个有用的快捷方式,但它不是在 VBA 中声明数组的标准方法。 (2认同)

Mic*_*onk 5

所有其他人都已经提到了使用 Dictionary 类的 scripting.runtime 版本。如果您无法使用此 DLL,您也可以使用此版本,只需将其添加到您的代码中即可。

https://github.com/VBA-tools/VBA-Dictionary/blob/master/Dictionary.cls

它与 Microsoft 的版本相同。