Словники (Dictionary), можливо більш відомі розробникам, які, в основному, працюють в середовищі VBScript, ніж людям, що використовують тільки Microsoft Office та VBA, представляють собою потужний і універсальний клас, корисний у застосуванні в багатьох випадках програмування.

В той час як рідний клас VBA — клас Collection — пропонує функціональність, багато в чому подібну класу Dictionary, останній може запропонувати багато додаткових переваг. Таким чином, в залежності від потреб вашої VBA процедури, клас Dictionary може вам надати привабливу альтернативу більш звичному класу Collection. В дійсності, навіть якщо додаткова функціональність Словника не має великого значення для вашого конкретного проекту, Словник може запропонувати перевагу над Колекцією в продуктивності (швидкодії).


Що ж являє собою клас Dictionary

ред.

Як складова частина бібліотеки Microsoft Scripting Runtime (scrrun.dll), клас Dictionary дає можливість створювати об'єкти, що містять довільну кількість елементів, де кожний елемент ідентифікується з унікальним ключем. Об'єкт Dictionary може містити елементи різних типів даних (у тому числі інші об'єкти, в тому числі інші Словники). Ключі (Keys) Dictionary також можуть бути визначені довільним типом даних, за винятком Array (тобто — окрім масивів), хоча, на практиці, до них майже завжди застосовуються типи змінних String або Integer / Long. Один об'єкт Dictionary може містити комбінацію елементів (Items) різних типів даних. Це ж стосується і ключів (Keys).

В процедурах, що використовують Словники, можливе наступне:

  • Додавання нових значень (Items) в Словники;
  • Видалення значень (Items) з Словників;
  • Отримання значень елементів (Items) Словників при допомозі посилань на пов'язані з цими елементами значення ключів (Keys);
  • Зміна значення (Item), яке пов'язане з певним ключем (Key);
  • Отримання значень всіх ключів (Keys), що використовуються в проекті і є дійсними на даний момент;
  • Отримання значення кількості ключів (Keys), що використовуються на поточний момент;
  • За необхідності, зміна значення ключа (Key).

Словники часто порівнюють з асоціативними масивами[1], які іноді називають «картами», «хешами», і/або «хеш-таблицяи») і які зустрічаються в таких мовах програмування, як Perl, JavaScript, C + +, Python. і т. д. Як правило, Словник використовується для зберігання елементів аналогічного характеру. Розглянемо, наприклад, наступний список:

Employee Date
Sonja 2008-06-13
Sonja 2008-03-28
Franklyn 2010-03-21
Adita 2009-05-03
Adita 2010-12-04
Tommy 2006-11-24
Sonja 2007-09-06
Tommy 2010-08-16
Kayin 2009-05-12
Adita 2008-06-18
Adita 2006-11-24

Припустимо, що ваша процедура має визначити найбільш ранню дату, пов'язану з кожним співробітником. У цьому випадку, ви можете:

  • Створити Словник;
  • Для кожного конкретного працівника (ст."Employee") знайти і додати значення (Item) дати (ст."Date"), яку з цим працівником пов'язати, використовуючи його ім'я в якості ключа (Key);
  • Переглядаючи список далі до низу, порівняти збережену дату з датою працівника в поточному рядку;
  • Якщо дата поточного рядка є більш ранішньою від збереженої дати попередньго рядка (при ідентичних ключах — імені працівника), виконується заміна збереженої дати поточною.

Чим різняться Словники і Колекції

ред.

VBA розробники визнають схожість класу Dictionary з класом Collection. Клас Колекція є рідним для бібліотеки VBA, і, таким чином, повністю інтегрований в мову. Отже, в ніяких спеціальних діях, необхідних для використання об'єкту Collection, не має потреби.

Як і при використанні Словника, при застосування Колекції ви можете:

  • Додати довільну кількість елементів довільного типу даних в Колекцію (як і у випадку з Словником, це можуть бути також Об'єкти, в тому числі інші Колекції);
  • Видаляти значення з Колекції;
  • Витягувати значення Колекції;
  • Отримати значення загальної кількості значень, що зберігаються в Колекції.

Однак, між Словниками і Колекціями існують наступні відмінності:

  • Застосування ключів в Словниках є обов'язковим. Ключі повинні бути унікальними для даного Словника. В Колекції ключі теж повинні бути унікальними, але вони необов'язкові для вжитку;
  • Витяг значення (Item) з Словника може бути здійснений тільки по ключу (Key), що пов'язаний з цим значенням. З Колекції значення (Item) може бути витягнуто як по значенню пов'язаного з ним ключа, так і по його індексу (наприклад, по порядковому номеру розташування в колекції, починаючи з одиниці);
  • Ключі Словника можуть бути представлені довільним типом даних. При використанні ключів з типом даних String, за замовчування встановлено метод порівняння vbBinaryCompare (тобто, чутливим до регістру, або CompareMode = 0), однак метод порівняння можна змінити на "нечутливий до регістру (vbTextCompare, або ж CompareMode = 1). У випадку Колекцій, ключі завжди повинні мати тип змінних String, і вони нечутливі до регістру, без можливості зміни способу порівняння CompareMode (див. Example #2: Distinct Values with Case-Sensitive Keys);
  • Для перевірки наявності в Словнику певного ключа (Key), використовується метод «Exists» (отже, і для перевірки значення (Item), що пов'язаний з цим ключем). Колекції не мають аналогічного методу. Натомість, при спробі отримати значення, що пов'язане з неіснуючим ключем, використовують обробку згенерованого системою повідомлення про помилку;
  • Розробники завжди можуть витягнути з Словника як ключ (Key), так і пов'язане значення (Item). Елементи (Items) Колекції доступні для «витягання», але ключі (Keys) — ні. Отже, при необхідності отримання значення (Item) і асоційованого з ним ключа, клас Dictionary надає більш легку можливість, ніж клас Collection;
  • Властивості значення елемента Словника (Item) встановлені за замовчуванням як read/write, що передбачає можливість зміни значення (Item), асоційованого з певним ключем. Властивість значення Колекції (Item) встановлено як read-only, і тому ви не маєте змоги перепризначити елемент, пов'язаний з зазначеним ключем. Натомість, ви вимушені видалити цей елемент з Колекції, і на його місце додати новий елемент;
  • Словник дозволяє змінювати значення ключа (Key) (не плутати з зміною значення (Item), пов'язаного з цим ключем!). Колекція не дозволить вам цього зробити. У випадку Колекції, єдине що ви можете зробити в подібній ситуації — видалити елемент, що відповідає поточному ключу, а потім записати цей же ж елемент з новим значенням ключа;
  • Словник дає можливість видалити однією дією всі значення, із збереженням самого Словника, як об'єкта. При застосуванні Колекції, Вам прийдеться або почергово видаляти кожний елемент, або знищити дану Колекцію з подальшим її відтворенням;
  • Обидва класи, як Collection, так і Dictionary, підтримують перебір елементів за допомогою циклу For … Each … Next. Однак, у випадку Колекції, перераховуватимуться елементи, у випадку Словника — ключі. Таким чином, при використанні циклу For … Each … Next для перерахунку елементів (Items) Словника, необхідне уточнення: 1: For Each x In MyDictionary 2: MsgBox MyDictionary.Item(x) 3: Next
  • Словник підтримує неявне додавання елемента за допомогою Item property. В Колекцію елементи повинні бути додані явно.

Для відносно простих потреб, таких, як визначення лише окремих елементів списку, переваг у використанні Словника, з точки зору функціональності, немає. Однак, якщо вам необхідно:

  • Отримати ключі, а також елементи, пов'язані з цими ключами;
  • Опрацювати чутливі до регістру ключі і/або
  • мати змогу реагувати на зміни елементів (Items) і/або ключів (Keys) в таких випадках використання Словників надає вам чудову альтернативу використанню Колекцій.

Тим не менш, навіть для відносно простих потреб, Словник може запропонувати значну перевагу в продуктивності, як це буде проаналізовано в кінці даної статті. Нарешті, якщо існує ймовірність, що код потрібно буде імпортувати у VBScript, рекомендується застосування Словника замість Колекції. Клас Collection відсутній в VBScript як такий, тому транспортування коду, що використовує Колекцію, в VBScript, на відміну від Словника, буде фактично неможливе.

Раннє (Early Binding) або пізнє (Late Binding) зв'язування

ред.

Оскільки клас Dictionary не є частиною бібліотеки VBA, для використання Словників у ваших VBA проектах ви будете вимушені використовувати т.з. раннє, або пізнє зв'язування. Для використання Early Binding, у вікні Refereces вкладки Tools редактора VBA необхідно додати посилання на бібліотеку Microsoft Scripting Runtime, яка вам надасть вільний доступ до класів, констант, властивостей, методів і т. д., що визначені в цій бібліотеці. Наприклад, при використанні раннього зв'язування, можна оголосити і створити Словник наступним чином:

1. Dim MyDictionary As Scripting.Dictionary
2. Set MyDictionary = New Scripting.Dictionary

Раннє зв'язування також дозволяє авто-завершення виразів при введенні коду і проекти з використанням раннього зв'язування, як правило, мають більш високу швидкість виконання, ніж проекти з використанням пізнього зв'язування. При застосуванні пізнього зв'язування (Late Binding), пряме посилання на зовнішні бібліотеки не встановлюється і, таким чином, при оголошенні змінної ви будете використовувати загальний тип Object, а для створення екземпляра класу Dictionary вам доведеться використовувати оператор CreateObject:

1. Dim MyDictionary As Object
2. Set MyDictionary = CreateObject("Scripting.Dictionary")

Якщо існує ймовірність, що бібліотеки комп'ютера, на якому виконується програмування, не будуть аналогічні бібліотекам, встановленим на комп'ютері кінцевого користувача, в таких випадках, зазвичай, розробники використовують пізнє зв'язування. Хоча в більшості випадків двигун VBA сам чудово справляється з різницею в налаштуваннях, однак застосування в подібних випадках пізнього зв'язування є більш надійним. Тим не менш, навіть тоді, коли кінцевий варіант коду використовуватиме пізнє зв'язування для мульти-підтримки програмного забезпечення комп'ютерного обладнання, розробники часто в процесі програмування застосовують раннє зв'язування з метою отримання доступу до додаткових можливостей редактора коду, а потім коригують код під застосування пізнього зв'язування на етапі підготовки коду для передачі кінцевим користувачам.

Клас Dictionary. Властивості і Методи

ред.

Як буде показано далі, клас Dictionary має чотири властивості і шість методів.

Add метод

ред.

Метод Add додає запис в Словник, і пов'язує елементи запису між собою:

1: MyDictionary.Add Key, Item

Елемент (Item) може бути чим завгодно: змінною будь-якого типу даних, об'єктом (в тому числі іншим Словником), або навіть масивом. Ключ (Key) може бути змінною будь-якого типу даних, за винятком масиву. Ключ (Key) повинен бути унікальним. Якщо ви спробуєте додати елемент (Item), використовуючи дублікат ключа (Key), ви отримаєте повідомлення про помилку виконання. За замовчуванням, ключі чутливі до регістру (тобто, CompareMode = 0). Щоб це змінити, вкажіть CompareMode рівним 1.

CompareMode властивість

ред.

Властивість CompareMode вказує, чи будуть текстові ключі Словника чутливими до регістру. За замовчуванням, значення CompareMode дорівнює нулю (чутливі до регістру). Щоб це змінити — використовуйте CompareMode = 1 (не чутливі до р-ру). Оскільки наведені значення збігаються з значеннями певних вбудованих VBA констант, можна використовувати також вирази:

1: MyDictionary.CompareMode = vbBinaryCompare ‘ case sensitive                                                          
2: MyDictionary.CompareMode = vbTextCompare ' case insensitive
                             

Таким чином, в наступному прикладі Словник сприйме вирази в рядках 3 і 4 як два різні ключі:

1: With MyDictionary
2: .CompareMode = vbBinaryCompare  ‘ sensitive
3: .Add "foo", "lower"  
4: .Add "FOO", "UPPER" 
5: End With

А якщо CompareMode дорівнює 1, тоді Словник буде бачити вирази в рядках 3 і 4 як одне і теж саме, і таким чином генеруватиме помилку виконання.

Count властивість

ред.

Властивість Count повертає кількість записів (пар Ключ/Значення) наявних на даний момент у Словнику. Якщо записи відсутні — поверне 0.

1: MsgBox "There are " & MyDictionary.Count & "items"

Exists метод

ред.

Метод Exists перевіряє існування вказаного ключа в Словнику, і повертає логічне значення True, якщо ключ існує, і False, якщо такого немає. Наведемо фрагмент тесту для перевірки існування ключа (Key) перед додаванням нового елемента (Item) в Словник:

1: With MyDictionary
2: If Not .Exists(SomeKey) Then  .Add SomeKey, SomeValue
3: End With

Клас Collection не має аналогічного методу для перевірки існування зазначеного ключа. Щоб зробити це, ви повинні спробувати витягнути елемент з Колекції, використовуючи цей ключ. Генерація помилки означатиме відсутність вказаного ключа:

1: On Error Resume Next
2: x = MyCollection("foo")
3: If Err = 0 Then
4: MsgBox x
5: Else
6: Err.Clear
7: MsgBox "There is no value associated" & "with 'foo'"
9: End If
10: On Error GoTo 0

Item властивість

ред.

Властивість (Item) отримує або задає елемент, пов'язаний із зазначеними ключем (Key):

1: With MyDictionary
2: .Item("SomeKey") = "foo"
3: MsgBox "The value for 'SomeKey' is '" & .Item("SomeKey")    							
4: End With

При використанні властивості Item, якщо буде спроба встановити елемент (Item) для неіснуючого ключа (Key), в Словник буде неявно додане значення (Item) разом з порожнім значенням ключа. Якщо ж тепер спробувати витягнути елемент, пов'язаний з неіснуючим на даний момент ключем — в словник буде додано порожній елемент, пов'язаний з цим ключем. Таким чином, при використанні властивості Item з неіснуючим ключем не буде згенероване повідомлення про помилку виконання.

Items метод

ред.

Метод Items повертає одновимірний масив елементів (Item), що зберігаються в даний час в Словнику (при цьому приймається Option base 0, навіть коли попередньо було декларовано Option base 1).

1: ' Returns a concatenated list of the Items:
2: 
3: MyArray = MyDictionary.Items
4: MsgBox Join(MyArray, ";")

Однак, не існує ніякої гарантії, що порядок елементів у масиві буде відповідати порядку, в якому ці елементи додавались в Словник.

Key властивість

ред.

Властивість Key (write-only) використовують для зміни/заміни значення існуючого ключа.

1: MyDictionary.Key("SomeKey") = "SomeOtherKey"

Новий ключ (Key) має бути унікальним для даного Словника. Ключ, який підлягає заміні/зміні, насправді має існувати в даному Словнику. Якщо хоча б одна з цих умов помилкова — буде згенеровано повідомлення про помилку виконання.

Keys метод

ред.

Метод Keys повертає одновимірний масив ключів (Key), що зберігаються в даний час в Словнику (при цьому приймається Option base 0, навіть коли попередньо було декларовано Option base 1).

1: ' Returns a concatenated list of the Keys:
2: 
3:  MyArray = MyDictionary.Keys
4:  MsgBox Join(MyArray, ";")

Не існує ніякої гарантії, що порядок ключів в масиві буде збігатися з порядком, в якому ці ключі були додані до Словника.

Remove метод

ред.

Метод Remove видаляє зі Cловника об'єкт (значення Item), пов'язаний з зазначеним ключем, а також і сам ключ.

1:   MyDictionary.Remove "SomeKey"

Якщо зазначений ключ не існує, виникає помилка.

RemoveAll метод

ред.

RemoveAll метод «очищає» Словник шляхом видалення всіх елементів (Item) з нього, а також пов'язані з ними ключі.

1: MyDictionary.RemoveAll

Сам об'єкт Dictionary не знищується.

Примітки

ред.