VBA有字典结构吗?就像键<>值数组一样?
是的。
设置对MS Scripting runtime ('Microsoft Scripting Runtime')的引用。根据@regjo的评论,转到工具-> 引用并选中'Microsoft Scripting Runtime'的框。
使用以下代码创建一个字典实例: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()
。
Contains
写法:On Error Resume Next
_ col(key)
_ Contains = (Err.Number = 0)
。它的意思是,如果 col(key)
存在于集合中,则 Contains
返回 True;否则返回 False。 - TWiStErRobContainsKey
; 只读取调用的人可能会将其与检查它是否包含特定值混淆。 - jpmc26VBA没有内置字典的实现,但你仍然可以从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
一个有用的额外字典示例,可用于包含出现频率。
循环之外:
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的答案 基础上,我们可以构建一个不需要标签的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
。它仍然允许递归集合。你会注意到键总是第一个出现,因为在我的实现中这是强制性的并且更有意义。我也只使用了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
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
其他人已经提到了使用Scripting.Runtime版本的Dictionary类。如果您无法使用该DLL,您也可以使用此版本,只需将其添加到您的代码中。
https://github.com/VBA-tools/VBA-Dictionary/blob/master/Dictionary.cls
它与Microsoft的版本相同。
脚本运行时字典似乎存在一个漏洞,可能会在高级阶段破坏您的设计。
如果字典值是数组,则无法通过对字典的引用更新包含在数组中的元素的值。
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
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://dev59.com/NjYCtIcB2Jgan1znWZ_t#56705428
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撰写的答案详细介绍了HashTable以及为什么需要使用mscorlib.IEnumerable
(对mscorlib的早期绑定引用)来枚举键值对。
Dim dict As New Scripting.Dictionary
。没有引用的情况下,你需要使用后期绑定的CreateObject
方法来实例化这个对象。 - David Zemens