Есть ли в VBA структура словаря? Как массив ключей <>?
Есть ли в VBA структура словаря? Как массив ключей <>?
Ответы:
Да.
Установите ссылку на среду выполнения сценариев MS («среда выполнения сценариев Microsoft»). В соответствии с комментарием @ regjo, перейдите в Tools-> References и поставьте галочку для «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
keyed
.
Dim dict As New Scripting.Dictionary
без ссылки. Без ссылки вы должны будете использовать метод поздней привязки CreateObject
для создания экземпляра этого объекта.
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
Изменить 24 июня 2015 года : короче Contains()
благодаря @TWiStErRob.
Редактировать 25 сентября 2015 : добавлено Err.Clear()
благодаря @scipilot.
ContainsKey
; кто-то, читающий только вызов, может спутать его с проверкой того, что он содержит определенное значение.
Дополнительный пример словаря, который полезен для сдерживания частоты встречаемости.
Вне цикла:
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
поведение стало более похожим на a 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
Если по какой-либо причине вы не можете установить дополнительные функции в 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
ключевом слове Country
и Capital
должна быть объявлена как Варианты из-за использования Array()
, i
должна быть объявлена (и должна быть, если Option Explicit
она установлена), и счетчик цикла будет выбрасывать ошибку выхода за границы - безопаснее использовать UBound(Country)
для To
значения. Также, возможно, стоит отметить, что хотя Array()
функция является полезным сочетанием клавиш, это не стандартный способ объявления массивов в VBA.
Все остальные уже упоминали об использовании версии scripting.runtime класса Dictionary. Если вы не можете использовать эту DLL, вы также можете использовать эту версию, просто добавьте ее в свой код.
https://github.com/VBA-tools/VBA-Dictionary/blob/master/Dictionary.cls
Это идентично версии Microsoft.