VBA 有字典结构吗?像键<>值数组?
是的。
设置对 MS 脚本运行时(“Microsoft 脚本运行时”)的引用。根据@regjo 的评论,转到工具-> 参考并勾选“Microsoft 脚本运行时”框。
https://i.stack.imgur.com/ubtvU.png
使用以下代码创建字典实例:
Set dict = CreateObject("Scripting.Dictionary")
或者
Dim dict As New Scripting.Dictionary
使用示例:
If Not dict.Exists(key) Then
dict.Add key, value
End If
使用完毕后不要忘记将字典设置为 Nothing
。
Set dict = Nothing
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")
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
2015 年 6 月 24 日编辑:感谢 @TWiStErRob,缩短了 Contains()
。
2015 年 9 月 25 日编辑:感谢@scipilot 添加了Err.Clear()
。
ContainsKey
;只阅读调用的人可能会因为检查它是否包含特定值而混淆它。
VBA 没有字典的内部实现,但在 VBA 中,您仍然可以使用 MS 脚本运行时库中的字典对象。
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
一个额外的字典示例,可用于包含出现频率。
循环外:
Dim dict As New Scripting.dictionary
Dim MyVar as String
在一个循环内:
'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
检查频率:
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
在 cjrh's answer 的基础上,我们可以构建一个不需要标签的 Contains 函数(我不喜欢使用标签)。
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
对于我的一个项目,我编写了一组帮助函数以使 Collection
的行为更像 Dictionary
。它仍然允许递归集合。您会注意到 Key 总是排在第一位,因为它是强制性的,并且在我的实现中更有意义。我也只使用了 String
键。如果你愿意,你可以把它改回来。
放
我将其重命名为设置,因为它会覆盖旧值。
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
得到
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
有
发这个帖子的原因...
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
消除
如果它不存在则不会抛出。只要确保它被删除。
Private Sub cRemove(ByRef Col As Collection, Key As String)
If cHas(Col, Key) Then Col.Remove Key
End Sub
钥匙
获取一组键。
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
脚本运行时字典似乎有一个错误,可能会在高级阶段破坏您的设计。
如果字典值是一个数组,则不能通过对字典的引用来更新数组中包含的元素的值。
是的。对于 VB6、VBA (Excel) 和 VB.NET
所有其他人已经提到了 Dictionary 类的 scripting.runtime 版本的使用。如果您无法使用此 DLL,您也可以使用此版本,只需将其添加到您的代码中即可。
https://github.com/VBA-tools/VBA-Dictionary/blob/master/Dictionary.cls
它与微软的版本相同。
如果出于任何原因,您无法在 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
Dim
关键字,由于使用 Array()
,需要将 Country
和 Capital
声明为 Variants,应该声明 i
(如果设置了 Option Explicit
,则必须声明) ,并且循环计数器将抛出一个越界错误——使用 UBound(Country)
作为 To
值更安全。还可能值得注意的是,虽然 Array()
函数是一个有用的快捷方式,但它不是在 VBA 中声明数组的标准方法。
VBA 可以使用 Scripting.Runtime
的字典结构。
而它的实现实际上是一个花哨的——只需执行myDict(x) = y
,它就会检查字典中是否有键x
,如果没有,它甚至创造了它。如果它在那里,它会使用它。
它不会“大喊”或“抱怨”这个额外的步骤,“在引擎盖下”执行。当然,您可以通过 Dictionary.Exists(key)
显式检查是否存在密钥。因此,这 5 行:
If myDict.exists("B") Then
myDict("B") = myDict("B") + i * 3
Else
myDict.Add "B", i * 3
End If
与此 1 班轮 - myDict("B") = myDict("B") + i * 3
相同。一探究竟:
Sub TestMe()
Dim myDict As Object, i As Long, myKey As Variant
Set myDict = CreateObject("Scripting.Dictionary")
For i = 1 To 3
Debug.Print myDict.Exists("A")
myDict("A") = myDict("A") + i
myDict("B") = myDict("B") + 5
Next i
For Each myKey In myDict.keys
Debug.Print myKey; myDict(myKey)
Next myKey
End Sub
https://i.stack.imgur.com/L0KpJ.png
您可以通过 System.Collections.HashTable
访问非本机 HashTable
。
表示基于键的哈希码组织的键/值对的集合。
不确定您是否想在 Scripting.Dictionary
上使用它,但为了完整起见在此处添加。如果有兴趣,您可以查看这些方法,例如 Clone, CopyTo
例子:
Option Explicit
Public Sub UsingHashTable()
Dim h As Object
Set h = CreateObject("System.Collections.HashTable")
h.Add "A", 1
' h.Add "A", 1 ''<< Will throw duplicate key error
h.Add "B", 2
h("B") = 2
Dim keys As mscorlib.IEnumerable 'Need to cast in order to enumerate 'https://stackoverflow.com/a/56705428/6241235
Set keys = h.keys
Dim k As Variant
For Each k In keys
Debug.Print k, h(k) 'outputs the key and its associated value
Next
End Sub
@MathieuGuindon 的这个 answer 提供了大量关于 HashTable 的详细信息,以及为什么必须使用 mscorlib.IEnumerable
(对 mscorlib 的早期绑定引用)来枚举键:值对。
不定期副业成功案例分享
keyed
有不同的定义。Dim dict As New Scripting.Dictionary
。如果没有引用,您必须使用实例化此对象的后期绑定CreateObject
方法。