Есть ли в VBA структура словаря?

219

Существует ли у VBA структура словаря? Как массив ключей < > value?

Теги:
dictionary
data-structures
vb6

9 ответов

297
Лучший ответ

Да.

Установите ссылку на рабочую среду MS Scripting ("Microsoft Scripting Runtime"). В соответствии с комментарием @regjo перейдите в Tools-> "Ссылки" и поставьте галочку в поле "Время выполнения сценариев Microsoft".

Изображение 3710

Создайте экземпляр словаря, используя следующий код:

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 
  • 16
    Этот тип структуры данных предоставляется средой выполнения сценариев, а не VBA. По сути, VBA может использовать практически любой тип структуры данных, доступный для него через интерфейс COM.
  • 159
    Просто для полноты: вам нужно обратиться к «Microsoft Scripting Runtime», чтобы это работало (перейдите в Tools-> References) и отметьте его.
Показать ещё 14 комментариев
165

У 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.

  • 5
    Хорошо сделано для указания на встроенный объект Collection, который можно использовать в качестве словаря, поскольку метод Add имеет необязательный аргумент «ключ».
  • 8
    Недостатком объекта коллекции является то, что вы не можете проверить, есть ли ключ в коллекции. Это просто выдаст ошибку. Это большая вещь, мне не нравятся коллекции. (я знаю, что есть обходные пути, но большинство из них "некрасивые")
Показать ещё 9 комментариев
36

У VBA нет внутренней реализации словаря, но из VBA вы все равно можете использовать объект словаря из библиотеки сценариев MS Scripting.

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
28

Дополнительный словарь, который полезен для хранения частоты появления.

Вне цикла:

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
  • 1
    Дополнительная учебная ссылка: kamath.com/tutorials/tut009_dictionary.asp
  • 0
    Это был очень хороший ответ, и я использовал его. Однако я обнаружил, что не могу ссылаться на dict.Items (i) или dict.Keys (i) в цикле, как вы делаете. Мне пришлось хранить их (список элементов и список ключей) в отдельных переменных перед входом в цикл, а затем использовать эти переменные, чтобы получить нужные мне значения. Like - allItems = companyList.Items allKeys = companyList.Keys allItems (i) Если нет, я получил бы ошибку: «Процедура свойства let не определена, а процедура get свойства не возвращала объект» при попытке доступа к ключам (i) или Элементы (я) в цикле.
9

Создав cjrh answer, мы можем построить функцию Содержит, не требующую ярлыков (мне не нравится использовать метки).

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. Вы можете изменить его, если хотите.

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
6

Сценарий времени выполнения скриптов, похоже, имеет ошибку, которая может испортить ваш дизайн на продвинутых этапах.

Если значение словаря является массивом, вы не можете обновлять значения элементов, содержащихся в массиве, через ссылку на словарь.

  • 0
    это правда?
6

Да. Для VB6, VBA (Excel) и VB.NET

  • 14
    Не уверены, что VB.NET имеет отношение к VBA?
  • 2
    Вы можете прочитать вопросы еще: я спросил о VBA: Visual Basic для приложений, а не для VB, не для VB.Net, не для любого другого языка.
Показать ещё 7 комментариев
4

Если по какой-либо причине вы не можете установить дополнительные функции в свой 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
  • 1
    Концепция этого ответа обоснована, но пример кода не будет работать так, как написано. Каждая переменная нуждается в своем собственном ключевом слове Dim , Country и Capital должны быть объявлены как Варианты из-за использования Array() , i должен быть объявлен (и должен быть, если установлен Option Explicit ), и счетчик цикла собирается бросить ошибка вне границы - безопаснее использовать UBound(Country) для значения To . Также, возможно, стоит отметить, что хотя функция Array() является полезным сочетанием клавиш, это не стандартный способ объявления массивов в VBA.
2

Все остальные уже упоминали использование версии scripting.runtime класса Dictionary. Если вы не можете использовать эту DLL, вы также можете использовать эту версию, просто добавьте ее в свой код.

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

Он идентичен версии Microsoft.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню