Чи має структура VBA словник?


Відповіді:


341

Так.

Встановіть посилання на час виконання сценарію MS Scripting ("Microsoft Scripting Runtime"). Відповідно до коментаря @ regjo, перейдіть до Інструменти-> Список літератури та поставте прапорець у розділі "Виконання сценарію Microsoft".

Вікно літератури

Створіть екземпляр словника за допомогою наведеного нижче коду:

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 

17
Цей тип структури даних забезпечується сценарієм виконання, а не VBA. В основному VBA може використовувати практично будь-який тип структури даних, який доступний до нього через COM-інтерфейс.
David-W-Fenton

163
Тільки задля повноти: вам потрібно посилатися на "Програму виконання сценарію Microsoft", щоб це працювало (перейдіть до Інструменти-> Посилання) і встановіть прапорець.
regjo

7
Ага, колекції VBA ВЗАЄМО. Але, можливо, ми маємо інше визначення keyed.
Девід-W-Fenton

8
Я використовую Excel 2010 ... але без посилання на "Інструменти Microsoft Scripting Runtime" - Посилання. Просто створення CreateObject НЕ працює. Отже, @masterjo Я думаю, що ваш коментар вище неправильний. Якщо я чогось не пропускаю .. Отже, хлопці Інструменти -> посилання потрібні.
ihightower

4
Як FYI, ви не можете використовувати цей Dim dict As New Scripting.Dictionaryфайл без посилання. Без посилання вам доведеться використовувати CreateObjectметод пізнього прив’язки для інстанції цього об'єкта.
Девід Земенс

179

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
Погано в об'єкті колекції те, що ви не можете перевірити, чи є ключ уже в колекції. Це просто призведе до помилки. Це велика річ, мені не подобається в колекціях. (я знаю, що існують обхідні шляхи, але більшість з них є "потворними")
MiVoth

5
Зауважте, що пошук рядкових клавіш (наприклад, c.Item ("Key2")) у словнику VBA IS хеширується, але пошук за цілим індексом (наприклад, c.Item (20)) не є - це лінійний для / next пошук стилів і цього слід уникати. Найкраще використовувати колекції лише для пошуку ключових рядків або для кожної ітерації.
Бен Макінтайр

4
Я знайшов коротший Contains: On Error Resume Next_ col(key)_Contains = (Err.Number = 0)
TWiStErRob

5
Можливо, функцію слід назвати ContainsKey; хтось, читаючи лише виклик, може переплутати його, перевіривши, чи містить воно певне значення.
jpmc26

44

VBA не має внутрішньої реалізації словника, але з VBA ви все ще можете використати об’єкт словника з бібліотеки MS Scripting Runtime.

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

29

Додатковий приклад словника, який корисний для вмісту частоти виникнення.

Поза петлі:

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
Джон М

Це була дуже гарна відповідь, і я її використав. Однак я виявив, що не можу посилатися на dict.Items (i) або dict.Keys (i) у циклі, як і ви. Мені довелося зберігати ці дані (список елементів та список ключів) в окремі зміни, перш ніж входити в цикл, а потім використовувати ці пара, щоб дістатися до потрібних мені значень. Як - allItems = companyList.Items allKeys = companyList.Keys allItems (i) Якщо ні, я отримаю помилку: "Процедура дозволеного властивості не визначена, а процедура отримання властивості не повертає об'єкт" при спробі отримати доступ до ключів (i) або Елементи (i) у циклі.
raddevus

10

Будівництво від відповіді 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

Для свого проекту я написав набір допоміжних функцій, щоб змусити Collectionсебе поводитись більше як Dictionary. Він все ще дозволяє рекурсивні колекції. Ви помітите, що Ключ завжди стає першим, тому що він був обов'язковим і мав більше сенсу в моєму виконанні. Я також використовував лише 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

6

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

Якщо значення словника є масивом, ви не можете оновити значення елементів, що містяться в масиві, через посилання на словник.


6

Так. Для VB6 , VBA (Excel) та VB.NET


2
Ви можете прочитати питання ще: я запитав про VBA: Visual Basic для додатка, не для VB, не для VB.Net, не для будь-якої іншої мови.

1
fessGUID: потім знову слід прочитати відповіді! Цю відповідь можна використовувати і для VBA (зокрема, перше посилання).
Конрад Рудольф

5
Я визнаю. Я занадто швидко прочитав питання. Але я сказав йому, що він повинен знати.
Меттью Флашен

5
@Oorang, немає абсолютно ніяких доказів того, що VBA стає підмножиною VB.NET, правил backcompat в Office - уявіть собі, що намагаєтеся перетворити кожен макрос Excel, коли-небудь написаний.
Річард Ґадсден

2
VBA насправді є SUPERSET VB6. Він використовує той же DLL-ядро, що і VB6, але потім додає всілякі функціональні можливості для конкретних програм в Office.
David-W-Fenton

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

3

Усі інші вже згадували про використання версії scripting.runtime класу Словник. Якщо ви не можете використовувати цю DLL, ви також можете використовувати цю версію, просто додайте її до свого коду.

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

Він ідентичний версії Microsoft.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.