Vba excel коллекция или массив

 

borro

Пользователь

Сообщений: 198
Регистрация: 01.01.1970

Здравствуйте.

Расскажите или укажите, есть ли где сравнительное сопоставление трех понятий: массив, коллекция, словарь.
Хочу понять, когда и зачем их лучше использовать, в чем общее, в чем различие.

Изменено: borro26.01.2017 02:14:53

 

The_Prist

Пользователь

Сообщений: 14182
Регистрация: 15.09.2012

Профессиональная разработка приложений для MS Office

Общее: по сути это все массивы.
Различия: дофига. У коллекций свой набор методов, у словарей свой. У массивов в чистом виде по большому счету нет ничего. Но применимо к VBA чисто массив — arr() — можно заполнить очень быстро без цикла значением диапазона ячеек или формулами. Словари и коллекции требуют обязательного цикла для добавления элементов.
Преимущество коллекций(New Collection) — могут использоваться и в Windows и в MAC. А словари — только в Windows. Так же при добавлении элемента в коллекцию можно указать куда поместить новый элемент: до или после. Это дает возможность делать быструю сортировку уникальных значений.
Преимущество словарей: есть проверка наличия ключа встроенным свойством(Exists), что позволяет работать с ними быстрее. Так же можно настроить методы сравнения текста через свойство CompareMode. Если установить как 1 — то регистр не будет учитываться методом Exists при сравнении имеющихся в словаре элементов.

Но однозначно сказать что лучше, а что хуже нельзя. Все зависит от ситуации.

P.S. Не забывайте отписывать в своих темах, в которых получили ответы:

http://www.planetaexcel.ru/forum/index.php?PAGE_NAME=message&FID=1&TID=86715&TITLE_SEO=86715-kak-pol…
http://www.planetaexcel.ru/forum/index.php?PAGE_NAME=message&FID=1&TID=87222&TITLE_SEO=87222-ne-udae…

Иначе есть шанс потом ответы не получить…

Изменено: The_Prist25.01.2017 17:22:26

Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы…

 

borro

Пользователь

Сообщений: 198
Регистрация: 01.01.1970

 

borro

Пользователь

Сообщений: 198
Регистрация: 01.01.1970

Например, в некотором цикле будут собираться данные вида:
Артикул1 Наименование1 число1 число2 … число15
Артикул1 Наименование1 другоечисло1 другоечисло2 … другоечисло15
Артикул2 Наименование1 число1 число2 … число15
Артикул2 Наименование2 число1 число2 … число15

Используя язык баз данных, пара значений «Артикул» и «Наименование» являются ключом для каждой строки массива данных.
На каждой итерации получения строки данных надо в некий тип данных(массив, коллекция, словарь) вносить получаемые значения таким образом:
1. если в хранилище нет пары с ключом артикул+наименование, то надо в него вносить получаемую строку
2. если уникальная пара уже есть в хранилище, то надо к хранящимся числовым значениям прибавить числовые значения новой строки, используя данные об артикуле и наименовании

Вопрос — в данной задаче надо использовать какой из типов данных?

 

Alex_ST

Пользователь

Сообщений: 2746
Регистрация: 22.12.2012

На лицо ужасный, добрый внутри

С уважением, Алексей (ИМХО: Excel-2003 — THE BEST!!!)
<#0>

 

Alex_ST

Пользователь

Сообщений: 2746
Регистрация: 22.12.2012

На лицо ужасный, добрый внутри

ОГРОМНЫМ плюсом словарей является наличие в них методов Items и Keys возвращающих «одним махом» массивы ЗНАЧЕНИЙ и КЛЮЧЕЙ словаря
Да и вообще, у коллекций всего 1 свойство (.Count) и 4 метода (.Add .Item .Remove .Clear), а у словарей 4 свойства (.CompareMode .Count  .Item .Key) и 6 методов (.Add .Exists .Items .Keys .Remove .RemoveAll), что придаёт работе с ними значительно большую гибкость.
По приведённой выше ссылке я выкладывал файл-шпаргалку со свойствами, методами и примерами работы со словарями.

С уважением, Алексей (ИМХО: Excel-2003 — THE BEST!!!)
<#0>

 

Alex_ST

Пользователь

Сообщений: 2746
Регистрация: 22.12.2012

На лицо ужасный, добрый внутри

#7

26.01.2017 16:15:31

Цитата
borro написал:
Артикул1 Наименование1 число1 число2 … число15
Артикул1 Наименование1 другоечисло1 другоечисло2 … другоечисло15
Артикул2 Наименование1 число1 число2 … число15
Артикул2 Наименование2 число1 число2 … число15

Так что уникальное? Похоже, что Артикул?
Ну и используйте их как ключи к значениям (записям) словаря. А в каждой записи могут лежать как массивы, так и подчинённые словари.
Там по ссылке есть пример использования словаря словарей.
Добавление ключа с пустой записью происходит автоматически при попытке обращения к словарю по отсутствующему ключу.
Единственный минус словарей по сравнению с коллекциями — это отсутствие у метода .Add опциональных параметров [Before | After], которые позволяют достаточно просто производить сортировку записей внутри коллекции.
Но это нужно, имхо, достаточно редко.

С уважением, Алексей (ИМХО: Excel-2003 — THE BEST!!!)
<#0>

 

Sanja

Пользователь

Сообщений: 14838
Регистрация: 10.01.2013

#8

26.01.2017 16:30:00

Цитата
Alex_ST написал: Так что уникальное? Похоже, что Артикул?

Совсем не похоже.

Цитата
borro написал: Используя язык баз данных, пара значений «Артикул» и «Наименование» являются ключом для каждой строки массива данных

Уникальна ПАРА (Артикул и Наименование), поэтому в данном случае лучше использовать составной ключ (Артикул&Наименование)

Согласие есть продукт при полном непротивлении сторон.

 

Андрей VG

Пользователь

Сообщений: 11878
Регистрация: 22.12.2012

Excel 2016, 365

#9

26.01.2017 16:41:08

Доброе время суток.
Со словарями есть один не приятный момент, столкнулся случайно. Если выполнить

Код
Dictionary.RemoveAll
Set Dictionary = Nothing

, то это не приводит к автоматическому освобождению объектов-значений словаря. Они как-бы существуют вплодь до принудительного End, даже не до завершения метода, в котором создавался словарь. С коллекциями такого не происходит.

 

Alex_ST

Пользователь

Сообщений: 2746
Регистрация: 22.12.2012

На лицо ужасный, добрый внутри

#10

26.01.2017 16:47:34

Как-то я никогда особо освобождением памяти не заморачивался, хотя словари люблю и использую часто…
Обычно использую не явное позднее связывание типа

Код
With CreateObject("Scripting.Dictionary"): .CompareMode = vbTextCompare   ' создаем временный словарь
…
End With

Никогда никаких проблем не возникало. Может быть, при таком связывании их просто нет?
Или просто задачи не настолько тяжёлые, чтобы память ощутимо отожрать?

Изменено: Alex_ST26.01.2017 16:48:01

С уважением, Алексей (ИМХО: Excel-2003 — THE BEST!!!)
<#0>

 

Андрей VG

Пользователь

Сообщений: 11878
Регистрация: 22.12.2012

Excel 2016, 365

Алексей, дело не в памяти — её хватало. А вот блокировка объектов не снималась. Пришлось делать вариант на Collection. Столкнулся не в Excel — в VBA IBM Rational System Architect.

Изменено: Андрей VG26.01.2017 16:52:58

 

Hugo

Пользователь

Сообщений: 23251
Регистрация: 22.12.2012

#12

26.01.2017 20:36:52

Цитата
borro написал:
Вопрос — в данной задаче надо использовать какой из типов данных?

— словарь для ключей с индексами, и массив для данных. Массив можно использовать тот же исходный.

 

SuperCat

Пользователь

Сообщений: 2737
Регистрация: 21.12.2012

#13

26.01.2017 21:38:36

Цитата
Андрей VG написал:
то это не приводит к автоматическому освобождению объектов-значений словаря

Интересно, а как это узнали?

There is no knowledge that is not power

 

Sanja

Пользователь

Сообщений: 14838
Регистрация: 10.01.2013

#14

26.01.2017 21:44:11

Цитата
Андрей VG написал: А вот блокировка объектов не снималась

Я вот тоже буквально сегодня заметил нечто подобное. Если

для словаря используется раннее связывание

словарь объявляется переменной (Dim dic1: Set dic1 = CreateObject(«Scripting.Dictionary»)), то наблюдаются какие-то глюки при попытке добавить НЕ уникальный ключ. С конструкцией With CreateObject(«Scripting.Dictionary») такого не происходит

Изменено: Sanja26.01.2017 22:12:25

Согласие есть продукт при полном непротивлении сторон.

 

The_Prist

Пользователь

Сообщений: 14182
Регистрация: 15.09.2012

Профессиональная разработка приложений для MS Office

#15

26.01.2017 21:49:52

Цитата
Sanja написал:
используется раннее связывание (Dim dic1: Set dic1 = CreateObject(«Scripting.Dictionary»)

Сёма, это-таки позднее связывание :) Раннее будет так:

Код
Dim dic1: Set dic1 = New Scripting.Dictionary

А при случае использования With очищение памяти от объектов происходит более корректно, когда-то мы вроде это обсуждали уже. Это касается не только словарей, кстати.  

Изменено: The_Prist26.01.2017 21:51:49

Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы…

 

Sanja

Пользователь

Сообщений: 14838
Регистрация: 10.01.2013

Ага, Семёёён Семёёёныч  :). Но факт наблюдался

Согласие есть продукт при полном непротивлении сторон.

 

Alex_ST

Пользователь

Сообщений: 2746
Регистрация: 22.12.2012

На лицо ужасный, добрый внутри

#17

26.01.2017 22:09:30

Цитата
The_Prist написал: Не забывайте отписывать в своих темах…

Похоже, что Дмитрий у нас провидец…
borro заварил кашу и исчез. А мы тут его о чём-то спрашиваем, что-то советуем, дискуссию о высоких материях ведём…

С уважением, Алексей (ИМХО: Excel-2003 — THE BEST!!!)
<#0>

 

Андрей VG

Пользователь

Сообщений: 11878
Регистрация: 22.12.2012

Excel 2016, 365

#18

26.01.2017 22:13:49

Цитата
SuperCat написал: Интересно, а как это узнали?

Да есть в этом RSA возможность делать перекрёстную связь между объектами. Пользователь добавляет ссылку с родительского объекта на дочерний и у дочернего появляется в свойствах ссылка на родительский. Соответственно, в VBA нужно ссылки добавлять только с одной стороны, иначе, так как объекты (родительский и дочерний) при их создании открыты на редактирование, то невозможно сохранить не тот не другой. При загрузке же данных описание связей идёт как попало. Вот я и сделал поиск родительского объекта для дочерних через словарь. Сохранил родительские, очистил словарь. Стал сохранять дочерние с установленными ссылками на родительские, а RSA выбрасывает объект заблокирован. Переделал на коллекции — ушло.

 

borro

Пользователь

Сообщений: 198
Регистрация: 01.01.1970

Доброго утра. Спасибо,

Sanja

за совет.
Дело в том, что остальные обсуждения моего последнего вопроса не касались, поэтому я молчал. Да и что отвечать, тут только впитывать информацию. Простите, если что не так :)

Изменено: borro28.01.2017 10:14:27

 

The_Prist

Пользователь

Сообщений: 14182
Регистрация: 15.09.2012

Профессиональная разработка приложений для MS Office

#20

28.01.2017 11:19:13

Цитата
borro написал:
Дело в том, что остальные обсуждения моего последнего вопроса не касались

А вопрос был какой последний?

Цитата
borro написал:
Вопрос — в данной задаче надо использовать какой из типов данных?

Вы все же получили после этого ответ от Alex_ST — словари.

Цитата
Alex_ST написал:
Посмотрите в топике  Dictionary — это совсем не сложно!

Но отписаться понятно-непонятно, оно-не оно уже не посчитали нужным. А между тем это проявление элементарной вежливости и уважения к отвечающим — отписаться ответившему: помогло — не помогло, понятно — не понятно и т.п. Чтобы все понимали имеет ли смысл дальше что-то предлагать или обсуждать. Т.к. Вы этого не делаете — тема развивается своим ходом, а Вас берут на заметку — типа это тот человек, который темы создает, ответы получает и сваливает. В результате что будет? Правильно. Вам просто перестанут отвечать, ибо потребительское отношение не нравится никому…

Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы…

 

Hugo

Пользователь

Сообщений: 23251
Регистрация: 22.12.2012

#21

28.01.2017 11:50:10

Есть одна тонкость с

Цитата
Alex_ST написал:
ОГРОМНЫМ плюсом словарей является наличие в них методов Items и Keys возвращающих «одним махом» массивы ЗНАЧЕНИЙ и КЛЮЧЕЙ словаря

— Microsoft не обещает точное соответствие этих двух массивов, т.е. вываливать их рядом небезопасно.
В ответственных задачах нужно к каждому ключу получать значение в индивидуальном порядке. Да и transpose на больших объёмах может сглючить…

Изменено: Hugo28.01.2017 12:01:49
(буковка потерялась…)

 

The_Prist

Пользователь

Сообщений: 14182
Регистрация: 15.09.2012

Профессиональная разработка приложений для MS Office

Согласен с Игорем. Лучше создавать под это отдельный массив и в цикле выгружать туда сначала ключ, а потом соответствующий ключу элемент.

Даже самый простой вопрос можно превратить в огромную проблему. Достаточно не уметь формулировать вопросы…

 

Юрий М

Модератор

Сообщений: 60575
Регистрация: 14.09.2012

Контакты см. в профиле

Не совсем по теме, но близко ))
Недавно столкнулся с тем, что перебор коллекции циклом работает очень медленно: раньше приходилось обрабаывать относительно небольшие по размеру коллекции, а тут попалась довольно большая. Так вот: уже после 1000-го элемента коллекции увидел, что процесс «не летает» )) А нужно было перебрать порядка 30 000 элементов. ZVI подсказал, что не следует перебирать коллекции по индексу (For i = 1 to Коллекция.Count) — скорость нелинейно падает. Для перебора коллекции следует использовать цикл For Each — Next. Переделал — перемена поразительна!
Лично я этого не знал — может кому ещё пригодится )

 

webley

Пользователь

Сообщений: 1995
Регистрация: 01.01.1970

Юрий М, очень пригодилось, спасибо!
Единственно от себя дабавлю — это также справедливо и для словарей. Вложенный цикл по двум словарям (для каждого элемента первого перебирались все элементы второго, 3000 и 1500 элементов соответственно) — for j=0 to count просто «умер», после замены на for each — даже не заметно на фоне остальных операций

 

Alex_ST

Пользователь

Сообщений: 2746
Регистрация: 22.12.2012

На лицо ужасный, добрый внутри

#25

01.07.2022 14:53:39

Цитата
Юрий М написал:
Лично я этого не знал — может кому ещё пригодится )

Что-то я этот совет Юрия либо пропустил, либо просто забыл…
Сейчас практически не программирую. Но несколько лет назад всё равно почему-то предпочитал в своих разработках применять циклы For Each — Next. Наверное, интуитивно  :)

С уважением, Алексей (ИМХО: Excel-2003 — THE BEST!!!)
<#0>

 

Jack Famous

Пользователь

Сообщений: 10848
Регистрация: 07.11.2014

OS: Win 8.1 Корп. x64 | Excel 2016 x64: | Browser: Chrome

#26

01.07.2022 17:10:51

Цитата
Alex_ST: почему-то предпочитал в своих разработках применять циклы For Each — Next

они надёжнее, позволяют собирать новый или изменять текущий массив по имеющемуся в цикле индексу. Например, цикл по тысячам областей (фильтр столбца по видимым) нельзя делать через For Each, потому что будет ошибка.

Честно говоря, есть сомнения по поводу скорости перебора словаря (коллекции не использую). Может, при позднем подключении, если только (я раннее использую). Тестов не вижу))

Изменено: Jack Famous01.07.2022 17:12:13

Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄

 

Jack Famous

Пользователь

Сообщений: 10848
Регистрация: 07.11.2014

OS: Win 8.1 Корп. x64 | Excel 2016 x64: | Browser: Chrome

#27

01.07.2022 17:31:22

Действительно, перебор по индексам «в лоб» — не для словарей.
Не понимаю, зачем это может быть нужно, но, если всё-таки нужно, то взять массивы из словаря и перебирать их будет многократно (на примере — в 8 раз для 100 тыс ключей) быстрее

Файл и Код

При раннем связывании, разница в соответствии «ключ-значение» в массивах при их получении из словаря, не замечена. При позднем — я не уверен.
При этом ПОРЯДОК РАСПОЛОЖЕНИЯ ПАР элементов в словаре, вполне может не соответствовать их ПОРЯДКУ при НАПОЛНЕНИИ словаря

Что я имею в виду

Ссылки по теме:

bedvit про порядок ключей-значений
Словарь (Scripting.Dictionary). Соответствие массивов ключей и элементов между собой
Получить значение по ключу. Массив, Словарь и Коллекция, Get Item by Key. Array vs Dictionary vs Collection

Изменено: Jack Famous04.07.2022 09:37:34

Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄

 

ZVI

Пользователь

Сообщений: 4328
Регистрация: 23.12.2012

#28

02.07.2022 14:57:49

Цитата
Jack Famous написал: ‘dic.Keys(i) вообще не факт что равен arr(i)

Добрый день, Алексей.
Вы же сами в коде создали ключи словаря, начиная с нулевого, а не с первого, как в значении начального элемента массива.
В массиве точно так же значения массива, не совпадают с индексом массива: arr(0) <> 0
Это ошибка в представлении о логике кода, а не проблема словаря.
Код должен быть, например таким:

Код
Sub t()
  Dim dic As New Dictionary
  Dim arr(), i&
  arr = Array(0, 1, 2, 3, 4)
  For i = LBound(arr) To UBound(arr)
    dic(i) = "Item " & i
    Debug.Print dic.Keys(i) = arr(i), dic.Keys(i), arr(i)
  Next
End Sub
' Или таким
Sub t1()
  Dim dic As New Dictionary
  Dim arr(), i&
  arr = Array(1, 2, 3, 4, 5)
  For i = LBound(arr) To UBound(arr)
    dic(arr(i)) = "Item " & i
    Debug.Print dic.Keys(i) = arr(i), dic.Keys(i), arr(i)
  Next
End Sub

Цитата
Jack Famous написал: При этом ПОРЯДОК РАСПОЛОЖЕНИЯ ПАР элементов в словаре, вполне может не соответствовать их ПОРЯДКУ при НАПОЛНЕНИИ словаря

Это утверждение некорректное, так как касается внутреннего механизма работы словаря, а не его документированного внешнего интерфейса (свойств и методов .Item, .Keys, .Values и др.)
Словари всегда обеспечивают соответствие в массивах .Keys и .Values порядку заполнения словаря.
В словаре всегда доступ по ключу соответствует доступу по индексу.
Об этом мы в теме по Вашей 2-й ссылке уже общались —

здесь

.

Изменено: ZVI02.07.2022 15:40:30

 

Jack Famous

Пользователь

Сообщений: 10848
Регистрация: 07.11.2014

OS: Win 8.1 Корп. x64 | Excel 2016 x64: | Browser: Chrome

#29

04.07.2022 09:36:47

ZVI, приветствую! Всегда рад видеть вас в своих (и не только) темах  :idea:

Цитата
ZVI: Вы же сами в коде создали ключи словаря, начиная с нулевого, а не с первого, как в значении начального элемента массива

спасибо, согласен, оплошал, поправил  :)

Цитата
ZVI: Словари всегда обеспечивают соответствие в массивах .Keys и .Values порядку заполнения словаря … уже общались

там были только тесты, а хотелось бы увидеть документальное подтверждение, ведь никакими тестами нельзя доказать правило, а можно только приближаться к нему. А вот когда в докУменте будет написано — другое дело. Во всяком случае, логика Виталия мне понятна: они сказали, что это аналог перловых таблиц, у перловых таблиц порядок сбора и хранения не соответствует, а значит и у словарей тоже.
Тестами пытался повторить, но не вышло — пока что всегда соответствует  :D

Изменено: Jack Famous04.07.2022 09:38:07

Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄

 

ZVI

Пользователь

Сообщений: 4328
Регистрация: 23.12.2012

#30

04.07.2022 22:28:09

Добрый вечер, Алексей.
Гипотеза — это предположение для объяснения каких-то явлений. Но явлений-то в данном случае и нет.
Слова Виталия, в принципе, могут касаться только внутреннего устройства словаря, а не его внешнего интерфейса (см. моё сообщение выше).
Но т.к. и Вы, и Виталий на форуме — авторитеты, то подобное утверждение про словари запросто может кого-то ввести в заблуждение. Чего не хотелось бы. Собственно, поэтому и подключаюсь в тему.

Здравствуйте, AndreA SN,

У массивов и коллекций имеются следующие плюсы и минусы:

Массивы

Плюсы:
Более высокое быстродействие по сравнению с коллекциями.
Занимают меньше памяти, т.к. не требуют хранения ссылок на каждый элемент массива.

Минусы:
Занимают фиксированную область в памяти. При необходимости можно выделить для массива новую область памяти с помощью ReDim Preserve, но тогда системе понадобится потратить время на перенос значений из старой области памяти в новую.
Могут хранить элементы только одного типа, так как при хранении различных типов невозможно (или очень трудоемко) вычислить позицию n-ого элемента массива.

Коллекции

Плюсы:
Занимают произвольную область памяти, могут хранить любое разумное количество элементов.
Могут хранить элементы различных типов, так как позиция элемента коллекции в памяти не вычисляется, а берется из ссылки.

Минусы:
Работают медленно.
Занимают больше памяти, так как помимо самих элементов коллекции требуется хранить еще ссылки на эти элементы.

Особенности массивов и коллекций
(это не плюсы и не минусы, просто особенности)

Массив относится к типу данных значений. При копировании одной переменной типа массива в другую получаем новый независимый массив.

Коллекция — это ссылочный тип, поэтому…

  • в первых, перед первым использованием необходимо выполнить выделение памяти под коллекцию и присвоить переменной ссылку на выделенную область памяти:
    Visual Basic
    1
    
    Set my_collection = New Collection
  • во-вторых, при присваивании одной переменной типа коллекции значения другой не создается новой независимой коллекции — вместо этого обе переменные будут хранить ссылку на одну и ту же область памяти.

С уважением,

Аксима

Содержание

  1. Зачем использовать массивы в VBA, когда есть коллекции?
  2. 3 ответа
  3. Excel VBA Collections
  4. What is a Collection in VBA?
  5. Collections Versus Arrays
  6. Scope of a Collection Object
  7. Create a Collection, Add Items, and Access Items
  8. Loop Through Collection
  9. Add Item to Collection
  10. VBA Coding Made Easy
  11. Remove an Item from Collection
  12. Count the Number of Items in a Collection
  13. Test Collection for a Specific Value
  14. Pass a Collection to a Sub / Function
  15. Return a Collection from a Function
  16. Convert a Collection to an Array
  17. Convert an Array into a Collection
  18. VBA Code Examples Add-in

Зачем использовать массивы в VBA, когда есть коллекции?

многие люди используют массивы массивов в Excel/VBA для хранения списка данных. Тем не менее, есть объект коллекции, который, на мой взгляд, МНОГО БОЛЬШЕ более удобен (в основном: не нужно повторно определять длину списка).

Итак, я искренне спрашиваю себя, не хватает ли чего-то? Почему другие люди все еще используют массивы для хранения списка данных? Это просто похмелье прошлого?

3 ответа

Несколько причин использовать массивы вместо коллекций (или словарей):

  • вы можете легко передать массив в диапазон (и наоборот) с помощью Range(«A1:B12») = MyArray Коллекции
  • могут хранить только уникальные ключи, тогда как массивы могут хранить любое значение
  • коллекции должны хранить пару (ключ, значение), тогда как вы можете хранить все в массиве

Смотрите статью Chip Pearson о массивах для лучшего понимания

Лучше спросить, почему люди будут использовать коллекции над словарями (ok, коллекции являются стандартными VBA, тогда как вам нужно импортировать словари)

@CharlesWilliams ответ правильный: цикл через все значения массива быстрее, чем повторение коллекции или словаря: настолько, что я всегда использую метод Keys() или Items() словаря, когда мне нужно сделайте это — оба метода возвращают векторный массив.

Примечание. Я использую класс Dictionary гораздо больше, чем использование коллекций, метод Exists() слишком полезен.

Есть или, конечно, недостатки в сборниках и словарях. Один из них состоит в том, что массивы могут быть 2- или даже 3-мерными — намного лучшая структура данных для табличных данных. Вы можете хранить массивы как члены коллекции, но есть некоторые недостатки: один из них заключается в том, что вы не можете получать ссылку на элемент — если вы не используете arrItem = MyDictionary (strKey) , вы почти наверняка получит «ByVal» копию массива; это плохо, если ваши данные динамичны и могут быть изменены несколькими процессами. Это также медленно: много выделения и освобождения.

Хуже всего то, что я не доверяю VBA, чтобы освободить память, если у меня есть коллекция или словарь с массивами (или объектами!) в качестве элементов: не из-за пределов области, а не из Set objCollection = Nothing , даже не objDictionary.RemoveAll — трудно доказать, что проблема существует с ограниченным набором инструментов тестирования, доступным в VBE, но я видел достаточно утечек памяти в приложениях, которые использовали массивы в словарях, чтобы знать, что вам нужно быть осторожным. При этом я никогда не использую массив без команды Erase.

@JMax объяснил другой большой плюс для массивов: вы можете заполнить массив в одном «ударе» на листе и записать свою работу в один «хит».

Вы можете, конечно, получить лучшее из обоих миров, построив класс Indexed Array: двумерный массив со связанными объектами коллекции или словаря, хранящий какой-то идентификатор строки в качестве ключей, и порядковые числа строк в качестве данных Предметы.

Источник

Excel VBA Collections

In this Article

This tutorial will demonstrate how to use collections in VBA.

What is a Collection in VBA?

A collection is an object that holds a number of similar items that can easily be accessed and manipulated, even if there are a large number of items within the collection.

You can create your own collections, but VBA also comes with built in collections such as the Sheets Collection, which stores every sheet in the workbook. By using a For Each loop, you can iterate through each worksheet in the Sheets Collection.

You can also address a specific worksheet in the collection using the index value (it’s position in the collection), or the actual name of the worksheet:

As worksheets are added or deleted so the Sheets collection grows or shrinks in size.

Note that with VBA collections the index number begins with 1 not with 0.

Collections Versus Arrays

Arrays and Collections can both store data, however they have several notable differences:

  1. Arrays are multidimensional whereas collections are only single dimension. You can dimension an array with several dimensions e.g.

This creates an array of 10 rows with 2 columns, almost like a worksheet. A collection is effectively a single column.

  1. When you populate your array, you need a separate line of code to put a value into each element of the array. If you had a two-dimensional array you would actually need 2 lines of code – one line to address the first column and one line to address the second column. With the Collection object, you simply use the Add method so that the new item is just added into the collection and the index value is automatically adjusted to suit.
  2. If you need to delete an item of data then it is more complicated in the array. You can set the values of an element to a blank value, but the element itself still exists within the array. If you are using a For Next loop to iterate through the array, the loop will return a blank value, which will need coding to make sure that the blank value is ignored. In a collection you use the Add or Remove methods, and all the indexing and re-sizing is automatically taken care of. The item that has been removed disappears completely. Arrays are useful for a fixed size of data, but collections are better for where the quantity of data is liable to change.
  3. Collections are Read Only whereas array values can be changed using VBA. With a collection, you would have to remove the value to be changed first and then add in the new changed value.
  4. In an array, you can only use a single data type for the elements which is set when you dimension the array. However, in the array you can use custom data types that you have designed yourself. You could have a very complicated array structure using a custom data type which in turn has several custom data types below it. In a collection, you can add use data types of data for each item. You could have a numeric value, a date, or a string – the collection object will take any data type. If you tried to put a string value in an array that was dimensioned as numeric, it would produce an error message.
  5. Collections are generally easier to use than arrays. In coding terms, when you create a collection object, it only has two methods (Add and Remove) and two properties (Count and Item), so the object is by no means complicated to program.
  6. Collections can use keys to locate data. Arrays do not have this function and require looping code to iterate through the array to find specific values.
  7. The size of an array needs to be defined when it is first created. You need to have an idea of how much data it is going to store. If you need to increase the size of the array you can use ‘ReDim’ to re-size it, but you need to use the keyword ‘Preserve’ if you do not want to lose the data already held in the array. A collection size does not need to be defined. It just grows and shrinks automatically as items are added or removed.

Scope of a Collection Object

In terms of scope, the collection object is only available whilst the workbook is open. It does not get saved when the workbook is saved. If the workbook is re-opened then the collection needs to be re-created using VBA code.

If you want your collection to be available to all the code in your code module, then you need to declare the collection object in the Declare section at the top of the module window.

This will ensure that all your code within that module can access the collection. If you want any module within your workbook to access the collection, then define it as a global object.

Create a Collection, Add Items, and Access Items

A simple collection object can be created in VBA using the following code:

The code creates a new object called ‘MyCollection’ and then the following lines of code use the Add method to add in 3 new values.

Loop Through Collection

You can then use code to iterate through each item in your collection:

Or this code will get the size of the collection with .Count and loop through each index number:

The first For Each Loop is faster than the second For Next loop but it only works in one direction (low index to high). The For Next Loop has the advantage that you can use a different direction (high index to low) and you can also use the Step method to change the increment. This is useful when you want to delete several items since you will need to run the deletion from the end of the collection to the start as the index will change as the deletions take place.

Add Item to Collection

The Add method in a collection has 3 optional parameters – Key, Before, and After.

You can use the ‘Before’ and ‘After’ parameters to define the position of your new item relative to the others already in the collection.

This is done by specifying the index number that you want your new item to be relative to.

In this example ‘Item2’ has been specified to be added before the first indexed item in the collection (which is ‘Item1’). When you iterate through this collection ‘Item2’ will appear first of all, followed by ‘Item1’ and ‘Item3’.

When you specify a ‘Before’ or ‘After’ parameter, the index value is automatically adjusted within the collection so that ‘Item2’ becomes index value of 1 and ‘Item1’ gets moved to an index value of 2.

You can also use the ‘Key’ parameter to add a reference value that you can use to identify the collection item. Note that a key value must be a string and must be unique within the collection.

‘Item2’ has been given a ‘Key’ value of ‘MyKey’ so that you can refer to that item using the value of ‘MyKey’ instead of the index number (2).

Note that the ‘Key’ value has to be a string value. It cannot be any other data type. Note that the collection is Read Only, and you cannot update the key value once it has been set. Also, you cannot check if a key value exists for a specific item in the collection or view the key value which is a bit of a drawback.

The ‘Key’ parameter has the added advantage of making your code more readable, especially if it is being handed over to a colleague to support, and you do not have to iterate through the entire collection to find that value. Imagine if you had a collection of 10,000 items how difficult it would be to reference one specific item!

VBA Coding Made Easy

Stop searching for VBA code online. Learn more about AutoMacro — A VBA Code Builder that allows beginners to code procedures from scratch with minimal coding knowledge and with many time-saving features for all users!

Remove an Item from Collection

You can use the ‘Remove’ method to delete items from your collection.

Unfortunately, it is not easy if the collection has a large number of items to work out the index of the item that you want to delete. This is where the ‘Key’ parameter comes in handy when the collection is being created.

When an item is removed from a collection the index values are automatically reset all the way through the collection. This is where the ‘Key’ parameter is so useful when you are deleting several items at once. For example, you could delete item index 105, and instantly item index 106 becomes index 105, and everything above this item has its index value moved down. If you use the Key parameter, there is no need to worry about which index value needs to be removed.

To delete all the collection items and create a new collection, you use the Dim statement again which creates an empty collection.

To remove the actual collection object completely, you can set the object to nothing.

This is useful if the collection is no longer required by your code. Setting the collection object to nothing removes all reference to it and releases the memory that it was using. This can have important implications on speed of execution of your code, if a large object is sitting in memory that is no longer required.

Count the Number of Items in a Collection

You can easily find out the number of items in your collection by using the ‘Count’ property.

You would use this property if you were using a For Next Loop to iterate through the collection as it will provide you with the upper limit for the index number.

Test Collection for a Specific Value

You can iterate through a collection to search for a specific value for an item using a For Each Loop.

The code creates a small collection, and then iterates through it looking for an item called ‘item2”. If found it displays a message box that it has found the specific item.

One of the drawbacks with this methodology is that you cannot access the index value or the key value.

If you use a For Next Loop instead, you can use the For Next counter to get the index value, although you still cannot get the ‘Key’ value.

The For Next counter (n) will provide the index position.

Sort Collection

There is no built-in functionality to sort a collection, but using some ‘out of the box’ thinking, code can be written to do a sort, utilizing Excel’s worksheet sorting function. This code uses a blank worksheet called ‘SortSheet’ to do the actual sorting.

This code first creates a collection with the items added in a random order. It then copies them into the first column on a worksheet (SortSheet).

The code then uses the Excel sort tool to sort the data in the column into ascending order. The code could also be modified to sort into descending order.

The collection is then emptied of data using a For Next Loop. Note that the step option is used so that it clears from the end of the collection to the start. This is because as it clears, the index values are reset, if it cleared from the start, it would not clear correctly (index 2 would become index 1)

Finally, using another For Next Loop, the item values are transferred back into the empty collection

A further For Each Loop proves out that the collection is now in good ascending order.

Unfortunately, this does not deal with any Key values that may have been entered originally, since the Key values cannot be read

Pass a Collection to a Sub / Function

A collection can be passed to a Sub or a Function in the same way as any other parameter

It is important to pass the collection using ‘ByRef’. This means that the original collection is used. If the collection is passed using ‘ByVal’ then this creates a copy of the collection which can have unfortunate repercussions.

If a copy is created using ‘ByVal’ then anything that changes the collection within the function only happens on the copy and not on the original. For example, if within the function, a new item is added to the collection, this will not appear in the original collection, which will create a bug in your code.

Return a Collection from a Function

You can return a collection from a function in the same way as returning any object. You must use the Set keyword.

This code creates a sub routine which creates an object called ‘MyCollection’ and then uses the ‘Set’ keyword to effectively call the function to populate that collection. Once this is done then it displays a message box to show the count of 2 items.

The function PopulateCollection creates a new collection object and populates it with 2 items. It then passes this object back to the collection object created in the original sub routine.

Convert a Collection to an Array

You may wish to convert your collection into an array. You may want to store the data where it can be changed and manipulated. This code creates a small collection and then transfers it into an array

Notice that the collection index starts at 1 whereas the array index starts at 0. Whereas the collection has 3 items, the array only needs to be dimensioned to 2 because there is an element 0

Convert an Array into a Collection

You may want to convert an array into a collection. For example, you may wish to access the data in a faster and more elegant manner that using code to get an array element.

Bear in mind that this will only work for a single dimension of the array because the collection has only one dimension.

If you did wish to use a multi-dimensional array, you could concatenate the array values together for each row within the array using a delimiter character between the array dimensions, so that when reading the collection value, you could programmatically use the delimiter character to separate the values out.

You could also move the data into the collection on the basis that the first-dimension value is added (index 1), and then the next dimension value is added (index 2) and so on.

If the array had, say, 4 dimensions, every fourth value in the collection would be a new set of values.

You could also add array values to use as keys (providing that they are unique) which would add an easy way of locating specific data.

VBA Code Examples Add-in

Easily access all of the code examples found on our site.

Simply navigate to the menu, click, and the code will be inserted directly into your module. .xlam add-in.

Источник

@CharlesWilliams answer is correct: looping through all the values of an array is faster than iterating a Collection or dictionary: so much so, that I always use the Keys() or Items() method of a dictionary when I need to do that — both methods return a vector array.

A note: I use the Dictionary class far more than I use collections, the Exists() method is just too useful.

There are, or course, drawbacks to collections and dictionaries. One of them is that arrays can be 2- or even 3-Dimensional — a much better data structure for tabulated data. You can store arrays as members of a collection, but there’s some downsides to that: one of them is that you might not be getting a reference to the item — unless you use arrItem = MyDictionary(strKey) you will almost certainly get a ‘ByVal’ copy of the array; that’s bad if your data is dynamic, and subject to change by multiple processes. It’s also slow: lots of allocation and deallocation.

Worst of all, I don’t quite trust VBA to deallocate the memory if I have a collection or dictionary with arrays (or objects!) as members: not on out-of-scope, not by Set objCollection = Nothing, not even by objDictionary.RemoveAll — it’s difficult to prove that the problem exists with the limited testing toolkit available in the VBE, but I’ve seen enough memory leaks in applications that used arrays in dictionaries to know that you need to be cautious. That being said, I never use an array without an Erase command somewhere.

@JMax has explained the other big plus for arrays: you can populate an array in a single ‘hit’ to the worksheet, and write back your work in a single ‘hit.

You can, of course, get the best of both worlds by constructing an Indexed Array class: a 2-dimensional array with associated collection or dictionary objects storing some kind of row identifier as the keys, and the row ordinals as the data items.

There are no control arrays in VBA like there are in VB. For certain controls you can create a custom class to handle the events. For example, assume you have a userform with two command buttons. In the userform module, put this code

Private mcolEventButtons As Collection

Private Sub UserForm_Initialize()

    Dim clsEventButton As CEventButton

    Set mcolEventButtons = New Collection

    Set clsEventButton = New CEventButton
    Set clsEventButton.EventButton = Me.CommandButton1
    clsEventButton.RangeAddress = "A1"
    mcolEventButtons.Add clsEventButton, Me.CommandButton1.Name

    Set clsEventButton = New CEventButton
    Set clsEventButton.EventButton = Me.CommandButton2
    clsEventButton.RangeAddress = "A10"
    mcolEventButtons.Add clsEventButton, Me.CommandButton2.Name

End Sub

Then create a custom class module named CEventButton and put this code

Private WithEvents mctlEventButton As MSForms.CommandButton
Private msRangeAddress As String

Public Property Set EventButton(ctlButton As MSForms.CommandButton)

    Set mctlEventButton = ctlButton

End Property

Public Property Get EventButton() As MSForms.CommandButton

    Set EventButton = mctlEventButton

End Property

Private Sub mctlEventButton_Click()

    Sheet1.Range(Me.RangeAddress).Value = "Something"

End Sub

Public Property Get RangeAddress() As String

    RangeAddress = msRangeAddress

End Property

Public Property Let RangeAddress(ByVal sRangeAddress As String)

    msRangeAddress = sRangeAddress

End Property

The WithEvents keyword in the variable dimensioning polls the commandbutton’s events and fires just as if it was tied to a particular control and in the userform module.

Here’s what you did: You created a Collection to hold instances of your custom class for as long as the userform is active. This ensures that those instances stay in scope. Then you created a new instance of the class, assigned a particular button to it, and saved it in the collection. You did the same for the next button. In this example there are only two buttons, but if you had more you could continue doing this until you run out of memory.

I create a RangeAddress property in the custom class module as an example. What information you’ll need to store will depend on what you’re ultimately trying to accomplish.

For this example to work, you need to set the ShowModal property of the userform to FALSE, have to commandbuttons named CommandButton1 and CommandButton2, have a sheet with a codename of Sheet1, and probably some other stuff.

In this Article

  • What is a Collection in VBA?
    • Collections Versus Arrays
  • Scope of a Collection Object
  • Create a Collection, Add Items, and Access Items
    • Loop Through Collection
    • Add Item to Collection
    • Remove an Item from Collection
    • Count the Number of Items in a Collection
    • Test Collection for a Specific Value
    • Pass a Collection to a Sub / Function
    • Return a Collection from a Function
    • Convert a Collection to an Array
    • Convert an Array into a Collection

This tutorial will demonstrate how to use collections in VBA.

What is a Collection in VBA?

A collection is an object that holds a number of similar items that can easily be accessed and manipulated, even if there are a large number of items within the collection.

You can create your own collections, but VBA also comes with built in collections such as the Sheets Collection, which stores every sheet in the workbook. By using a For Each loop, you can iterate through each worksheet in the Sheets Collection.

Sub TestWorksheets()
Dim Sh As Worksheet
For Each Sh In Sheets
    MsgBox Sh.Name
    MsgBox Sh.Visible
Next Sh
End Sub

You can also address a specific worksheet in the collection using the index value (it’s position in the collection), or the actual name of the worksheet:

MsgBox Sheets(1).Name
MsgBox Sheets("Sheet1").Name

As worksheets are added or deleted so the Sheets collection grows or shrinks in size.

Note that with VBA collections the index number begins with 1 not with 0.

Collections Versus Arrays

Arrays and Collections can both store data, however they have several notable differences:

  1. Arrays are multidimensional whereas collections are only single dimension. You can dimension an array with several dimensions e.g.
Dim MyArray(10, 2) As String

This creates an array of 10 rows with 2 columns, almost like a worksheet. A collection is effectively a single column.

  1. When you populate your array, you need a separate line of code to put a value into each element of the array. If you had a two-dimensional array you would actually need 2 lines of code – one line to address the first column and one line to address the second column. With the Collection object, you simply use the Add method so that the new item is just added into the collection and the index value is automatically adjusted to suit.
  2. If you need to delete an item of data then it is more complicated in the array. You can set the values of an element to a blank value, but the element itself still exists within the array.  If you are using a For Next loop to iterate through the array, the loop will return a blank value, which will need coding to make sure that the blank value is ignored.  In a collection you use the Add or Remove methods, and all the indexing and re-sizing is automatically taken care of. The item that has been removed disappears completely. Arrays are useful for a fixed size of data, but collections are better for where the quantity of data is liable to change.
  3. Collections are Read Only whereas array values can be changed using VBA. With a collection, you would have to remove the value to be changed first and then add in the new changed value.
  4. In an array, you can only use a single data type for the elements which is set when you dimension the array. However, in the array you can use custom data types that you have designed yourself. You could have a very complicated array structure using a custom data type which in turn has several custom data types below it.  In a collection, you can add use data types of data for each item.  You could have a numeric value, a date, or a string – the collection object will take any data type.  If you tried to put a string value in an array that was dimensioned as numeric, it would produce an error message.
  5. Collections are generally easier to use than arrays. In coding terms, when you create a collection object, it only has two methods (Add and Remove) and two properties (Count and Item), so the object is by no means complicated to program.
  6. Collections can use keys to locate data. Arrays do not have this function and require looping code to iterate through the array to find specific values.
  7. The size of an array needs to be defined when it is first created. You need to have an idea of how much data it is going to store. If you need to increase the size of the array you can use ‘ReDim’ to re-size it, but you need to use the keyword ‘Preserve’ if you do not want to lose the data already held in the array. A collection size does not need to be defined. It just grows and shrinks automatically as items are added or removed.

Scope of a Collection Object

In terms of scope, the collection object is only available whilst the workbook is open. It does not get saved when the workbook is saved. If the workbook is re-opened then the collection needs to be re-created using VBA code.

If you want your collection to be available to all the code in your code module, then you need to declare the collection object in the Declare section at the top of the module window.

PIC 01 new

This will ensure that all your code within that module can access the collection.  If you want any module within your workbook to access the collection, then define it as a global object.

Global MyCollection As New Collection

Create a Collection, Add Items, and Access Items

A simple collection object can be created in VBA using the following code:

Sub CreateCollection()
'Create Collection
Dim MyCollection As New Collection

'Add Items to Collection
MyCollection.Add "Item1"
MyCollection.Add "Item2"
MyCollection.Add "Item3"
End Sub

The code creates a new object called ‘MyCollection’ and then the following lines of code use the Add method to add in 3 new values.

Loop Through Collection

You can then use code to iterate through each item in your collection:

For Each Item In MyCollection
    MsgBox Item
Next Item

Or this code will get the size of the collection with .Count and loop through each index number:

For n = 1 To MyCollection.Count
    MsgBox MyCollection(n)
Next n

The first For Each Loop is faster than the second For Next loop but it only works in one direction (low index to high). The For Next Loop has the advantage that you can use a different direction (high index to low) and you can also use the Step method to change the increment. This is useful when you want to delete several items since you will need to run the deletion from the end of the collection to the start as the index will change as the deletions take place.

Add Item to Collection

The Add method in a collection has 3 optional parameters – Key, Before, and After.

You can use the ‘Before’ and ‘After’ parameters to define the position of your new item relative to the others already in the collection.

This is done by specifying the index number that you want your new item to be relative to.

Sub CreateCollection()
Dim MyCollection As New Collection
MyCollection.Add "Item1"
MyCollection.Add "Item2", , 1
MyCollection.Add "Item3"

End Sub

In this example ‘Item2’ has been specified to be added before the first indexed item in the collection (which is ‘Item1’). When you iterate through this collection ‘Item2’ will appear first of all, followed by ‘Item1’ and ‘Item3’.

When you specify a ‘Before’ or ‘After’ parameter, the index value is automatically adjusted within the collection so that ‘Item2’ becomes index value of 1 and ‘Item1’ gets moved to an index value of 2.

You can also use the ‘Key’ parameter to add a reference value that you can use to identify the collection item. Note that a key value must be a string and must be unique within the collection.

Sub CreateCollection()
Dim MyCollection As New Collection
MyCollection.Add "Item1"
MyCollection.Add "Item2", "MyKey"
MyCollection.Add "Item3"

MsgBox MyCollection("MyKey")
End Sub

‘Item2’ has been given a ‘Key’ value of ‘MyKey’ so that you can refer to that item using the value of ‘MyKey’ instead of the index number (2).

Note that the ‘Key’ value has to be a string value. It cannot be any other data type. Note that the collection is Read Only, and you cannot update the key value once it has been set.  Also, you cannot check if a key value exists for a specific item in the collection or view the key value which is a bit of a drawback.

The ‘Key’ parameter has the added advantage of making your code more readable, especially if it is being handed over to a colleague to support, and you do not have to iterate through the entire collection to find that value. Imagine if you had a collection of 10,000 items how difficult it would be to reference one specific item!

VBA Coding Made Easy

Stop searching for VBA code online. Learn more about AutoMacro — A VBA Code Builder that allows beginners to code procedures from scratch with minimal coding knowledge and with many time-saving features for all users!

automacro

Learn More

Remove an Item from Collection

You can use the ‘Remove’ method to delete items from your collection.

MyCollection.Remove (2)

Unfortunately, it is not easy if the collection has a large number of items to work out the index of the item that you want to delete. This is where the ‘Key’ parameter comes in handy when the collection is being created.

MyCollection.Remove (“MyKey”)

When an item is removed from a collection the index values are automatically reset all the way through the collection. This is where the ‘Key’ parameter is so useful when you are deleting several items at once. For example, you could delete item index 105, and instantly item index 106 becomes index 105, and everything above this item has its index value moved down. If you use the Key parameter, there is no need to worry about which index value needs to be removed.

To delete all the collection items and create a new collection, you use the Dim statement again which creates an empty collection.

Dim MyCollection As New Collection

To remove the actual collection object completely, you can set the object to nothing.

Set MyCollection = Nothing

This is useful if the collection is no longer required by your code.  Setting the collection object to nothing removes all reference to it and releases the memory that it was using.  This can have important implications on speed of execution of your code, if a large object is sitting in memory that is no longer required.

Count the Number of Items in a Collection

You can easily find out the number of items in your collection by using the ‘Count’ property.

MsgBox MyCollection.Count

You would use this property if you were using a For Next Loop to iterate through the collection as it will provide you with the upper limit for the index number.

Test Collection for a Specific Value

You can iterate through a collection to search for a specific value for an item using a For Each Loop.

Sub SearchCollection()
Dim MyCollection as New Collection

MyCollection.Add "Item1"
MyCollection.Add "Item2"
MyCollection.Add "Item3"

For Each Item In MyCollection
        If Item = "Item2" Then
            MsgBox Item & " Found"
        End If
Next
End Sub

The code creates a small collection, and then iterates through it looking for an item called ‘item2”. If found it displays a message box that it has found the specific item.

One of the drawbacks with this methodology is that you cannot access the index value or the key value.

If you use a For Next Loop instead, you can use the For Next counter to get the index value, although you still cannot get the ‘Key’ value.

Sub SearchCollection()
Dim MyCollection As New Collection
MyCollection.Add "Item1"
MyCollection.Add "Item2"
MyCollection.Add "Item3"

For n = 1 To MyCollection.Count
        If MyCollection.Item(n) = "Item2" Then
            MsgBox MyCollection.Item(n) & " found at index position " & n
        End If
Next n
End Sub

The For Next counter (n) will provide the index position.

Sort Collection

There is no built-in functionality to sort a collection, but using some ‘out of the box’ thinking, code can be written to do a sort, utilizing Excel’s worksheet sorting function. This code uses a blank worksheet called ‘SortSheet’ to do the actual sorting.

Sub SortCollection()
Dim MyCollection As New Collection
Dim Counter As Long

'Build collection with random order items
MyCollection.Add "Item5"
MyCollection.Add "Item2"
MyCollection.Add "Item4"
MyCollection.Add "Item1"
MyCollection.Add "Item3"

'Capture number of items in collection for future use
Counter = MyCollection.Count

'Iterate through the collection copying each item to a consecutive cell on ‘SortSheet’ (column A)
For n = 1 To MyCollection.Count
    Sheets("SortSheet").Cells(n, 1) = MyCollection(n)

Next n
'Activate the sortsheet and use the Excel sort routine to sort the data into ascending order
Sheets("SortSheet").Activate
Range("A1:A" & MyCollection.Count).Select
    ActiveWorkbook.Worksheets("SortSheet").Sort.SortFields.Clear
    ActiveWorkbook.Worksheets("SortSheet"). Sort.SortFields.Add2 Key:=Range( _
        "A1:A5"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
        xlSortNormal
    With ActiveWorkbook.Worksheets("SortSheet").Sort
        .SetRange Range("A1:A5")
        .Header = xlGuess
        .MatchCase = False
        .Orientation = xlTopToBottom
        .SortMethod = xlPinYin
        .Apply
    End With
'Delete all the items in the collection – note that this For Next Loop runs in reverse order
For n = MyCollection.Count To 1 Step -1
     MyCollection.Remove (n)
Next n

'Copy the cell values back into the empty collection object using the stored value (Counter) for the ‘loop
For n = 1 To Counter
    MyCollection.Add Sheets("SortSheet").Cells(n, 1).Value

Next n

'Iterate through the collection to prove out the order that the items are now in
For Each Item In MyCollection
    MsgBox Item

Next Item

'Clear the worksheet (sortsheet) – if necessary, delete it as well
Sheets("SortSheet").Range(Cells(1, 1), Cells(Counter, 1)).Clear
End Sub

This code first creates a collection with the items added in a random order. It then copies them into the first column on a worksheet (SortSheet).

The code then uses the Excel sort tool to sort the data in the column into ascending order. The code could also be modified to sort into descending order.

The collection is then emptied of data using a For Next Loop. Note that the step option is used so that it clears from the end of the collection to the start.  This is because as it clears, the index values are reset, if it cleared from the start, it would not clear correctly (index 2 would become index 1)

Finally, using another For Next Loop, the item values are transferred back into the empty collection

A further For Each Loop proves out that the collection is now in good ascending order.

Unfortunately, this does not deal with any Key values that may have been entered originally, since the Key values cannot be read

VBA Programming | Code Generator does work for you!

Pass a Collection to a Sub / Function

A collection can be passed to a Sub or a Function in the same way as any other parameter

Function MyFunction(ByRef MyCollection as Collection)

It is important to pass the collection using ‘ByRef’. This means that the original collection is used.  If the collection is passed using ‘ByVal’ then this creates a copy of the collection which can have unfortunate repercussions.

If a copy is created using ‘ByVal’ then anything that changes the collection within the function only happens on the copy and not on the original.  For example, if within the function, a new item is added to the collection, this will not appear in the original collection, which will create a bug in your code.

Return a Collection from a Function

You can return a collection from a function in the same way as returning any object. You must use the Set keyword.

Sub ReturnFromFunction()
Dim MyCollection As Collection
Set MyCollection = PopulateCollection
MsgBox MyCollection.Count
End Sub

This code creates a sub routine which creates an object called ‘MyCollection’ and then uses the ‘Set’ keyword to effectively call the function to populate that collection. Once this is done then it displays a message box to show the count of 2 items.

Function PopulateCollection() As Collection
Dim MyCollection As New Collection
MyCollection.Add "Item1"
MyCollection.Add "Item2"

Set PopulateCollection = MyCollection
End Function

The function PopulateCollection creates a new collection object and populates it with 2 items. It then passes this object back to the collection object created in the original sub routine.

Convert a Collection to an Array

You may wish to convert your collection into an array. You may want to store the data where it can be changed and manipulated. This code creates a small collection and then transfers it into an array

Notice that the collection index starts at 1 whereas the array index starts at 0. Whereas the collection has 3 items, the array only needs to be dimensioned to 2 because there is an element 0

Sub ConvertCollectionToArray()
Dim MyCollection As New Collection
Dim MyArray(2) As String

MyCollection.Add "Item1"
MyCollection.Add "Item2"
MyCollection.Add "Item3"

For n = 1 To MyCollection.Count
    MyArray(n - 1) = MyCollection(n)
 
Next n

For n = 0 To 2
    MsgBox MyArray(n)
Next n
End Sub

Convert an Array into a Collection

You may want to convert an array into a collection. For example, you may wish to access the data in a faster and more elegant manner that using code to get an array element.

Bear in mind that this will only work for a single dimension of the array because the collection has only one dimension.

Sub ConvertArrayIntoCollection()
Dim MyCollection As New Collection
Dim MyArray(2) As String
MyArray(0) = "item1"
MyArray(1) = "Item2"
MyArray(2) = "Item3"

For n = 0 To 2
    MyCollection.Add MyArray(n)

Next n
For Each Item In MyCollection
    MsgBox Item
Next Item
End Sub

If you did wish to use a multi-dimensional array, you could concatenate the array values together for each row within the array using a delimiter character between the array dimensions, so that when reading the collection value, you could programmatically use the delimiter character to separate the values out.

You could also move the data into the collection on the basis that the first-dimension value is added (index 1), and then the next dimension value is added (index 2) and so on.

If the array had, say, 4 dimensions, every fourth value in the collection would be a new set of values.

You could also add array values to use as keys (providing that they are unique) which would add an easy way of locating specific data.

На чтение 24 мин. Просмотров 33.7k.

 itemprop=

Леонард Лаудер

Я не строитель зданий, я строитель коллекций

Содержание

  1. Краткое руководство по коллекциям
  2. Введение
  3. Что такое коллекция?
  4. Коллекции против Массивов
  5. Как создать коллекцию
  6. Удаление всех элементов из коллекции
  7. Добавление предметов в коллекцию
  8. Доступ к элементам коллекции
  9. Добавление разных типов
  10. Добавление элементов с помощью ключа
  11. Доступ ко всем элементам в коллекции
  12. Сортировка коллекции
  13. Использование коллекций с функциями и подпрограммами
  14. Заключение

Краткое руководство по коллекциям

Задача Пример
Объявить Dim coll As Collection
Создать во время выполнения Set coll = New Collection
Объявить и создать Dim coll As New Collection
Добавить элемент coll.Add «Яблоко»
Доступ к элементу coll(1) or coll(2)
Элемент доступа добавлен в
первый раз
coll(1)
Доступ к элементу добавлен в
последний раз
coll(coll.Count)
Получить количество предметов coll.Count
Доступ ко всем предметам Dim i As Long
For i = 1 To coll.Count
   Debug.Print coll(i) 
Next i
Доступ ко всем предметам (For
Each)
Dim fruit As Variant
For Each fruit In coll
   Debug.Print fruit 
Next fruit
Удалить элемент coll.Remove(1)
Удалить все элементы Set coll = New Collection

Введение

Коллекции являются очень важной частью VBA. Если вы пользовались VBA какое-то время, вы будете использовать Коллекции. Наиболее распространенными из них являются
Workbooks, Worksheets, коллекции Range и Cells.

В следующем коде показаны некоторые примеры использования коллекции Workbooks VBA.

' Workbooks это коллекция всех открытых рабочих книг

    ' Count - это количество книг в коллекции.
    Debug.Print Workbooks.Count

    ' Напечатайте полное имя книги с именем Пример.xlsm
    Debug.Print Workbooks("Пример.xlsm").FullName

    ' Напечатайте полное название книги, которая была открыта второй
    Debug.Print Workbooks(2).FullName

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

Что такое коллекция?

Коллекции и массивы используются для группировки переменных. Они оба хранят набор похожих предметов, например список студенческих оценок или названий стран. Использование коллекции или массива позволяет быстро и легко манипулировать большим количеством предметов.

В своей статье о массивах я объяснил простым языком, что такое массивы и почему они так полезны. Я кратко изложу эту информацию здесь.

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

 Dim mark As Long
    mark = sheetMarks.Range("A1")

Однако в большинстве случаев вам придется иметь дело с несколькими студентами. Представьте, что вы хотите хранить оценки 100 учеников. Если вы не использовали коллекции или массивы, вам нужно было бы создать сотню переменных — одну переменную для хранения оценки для каждого учащегося.

Другая проблема заключается в том, что вы должны использовать эти переменные по отдельности. Если вы хотите сохранить 100 отметок, вам понадобится строка кода каждый раз, когда вы хотите сохранить значение в переменной.

' Объявите переменную для каждой оценки
    Dim mark1 As Long
    Dim mark2 As Long
    .
    .
    .
    Dim mark100 As Long

    ' Сохраните отметки на листе в переменной
    mark1 = sheetMarks.Range("A1")
    mark2 = sheetMarks.Range("A2")
    .
    .
    .
    mark100 = sheetMarks.Range("A100")

Как вы можете видеть в приведенном выше примере, написание такого кода означало бы сотни строк повторяющегося кода. Когда вы используете коллекцию или массив, вам нужно только объявить одну переменную. Использование цикла с коллекцией или массивами означает, что вам нужна только одна строка для добавления или чтения значений.

Если мы переписываем приведенный выше пример с использованием коллекции, нам нужно всего несколько строк кода

 ' Создать коллекцию
    Dim collMarks As New Collection

    ' Прочитайте 100 значений в коллекцию
    Dim c As Range
    For Each c In Sheet1.Range("A1:A100")
        ' Эта строка используется для добавления всех значений
        collMarks.Add c.Value
    Next

Коллекции против Массивов

Мы рассмотрели, что общего у коллекций и массивов. Так в чем же разница и зачем использовать один вместо другого?

Основное отличие состоит в том, что с массивом вы обычно устанавливаете размер один раз. Это означает, что вы знаете размер до того, как начнете добавлять элементы. Позвольте мне объяснить это на примере.

Пример: когда Массив лучше

Представьте, что у вас есть лист оценок учеников с одним учеником на строку:

VBA Collection

Вы хотите хранить информацию о каждом ученике. В этом примере вы можете легко посчитать количество строк, чтобы получить количество студентов. Другими словами, вы знаете количество предметов заранее.

 ' Получить последнюю строку - это количество студентов
    Dim lStudentCount As Long
    lStudentCount = Sheet1.Range("A" & Rows.Count).End(xlUp).Row

    ' Создать массив правильного размера
    Dim arr() As Long
    ReDim arr(1 To lStudentCount)

В примере кода, как видите, мы получаем количество студентов путем подсчета строк. Затем мы можем использовать это для создания массива правильного размера.

Массивы используются, когда размер фиксирован.

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

Пример: когда Коллекция лучше

В этом примере у нас есть один и тот же рабочий лист студента, но на этот раз нам нужны только студенты с заданными критериями. Например, только студенты из Твери или Москвы, которые изучают математику или историю. Другими словами, вы не будете знать, как выбрать ученика, пока не прочитаете его данные на рабочем листе.

Представьте также, что студенты могут быть добавлены или удалены из списка во время работы приложения.

Таким образом, в этом примере количество студентов не является фиксированным и сильно меняется. Здесь вы не знаете количество студентов заранее. Поэтому вы не знаете, какой размер массива создать.

Вы можете создать массив максимально возможного размера. Проблема в том, что у вас будет много пустых слотов, и вам придется добавить код, чтобы справиться с ними. Если вы прочитаете 50 учеников с максимумом 1000, то у вас будет 950 неиспользуемых слотов массива.

Вы также можете изменить размер массива для каждого элемента по мере его добавления. Это очень неэффективно и довольно грязно.

Так что для этого примера лучше использовать коллекцию.

 ' Объявить
    Dim coll As New Collection

    ' Добавить элемент - VBA следит за изменением размера
    coll.Add "Яблоко"
    coll.Add "Слива"

    ' удалить элемент - VBA следит за изменением размера
    coll.Remove 1

Когда вы добавляете или удаляете элемент в коллекцию, VBA выполняет все изменения размера за вас. Вам не нужно указывать размер или выделять новые пробелы. VBA делает это самостоятельно. Все, что вам нужно сделать, это добавить элемент или удалить его.

Коллекция используется, когда размер часто изменяется.

Еще одно преимущество коллекций

Коллекции гораздо проще использовать, чем массивы, особенно если вы новичок в программировании. Большую часть времени вы делаете три вещи с коллекциями:

  1. Создать коллекцию
  2. Добавьте несколько предметов
  3. Прочитайте предмет

Так что, если вы не имеете дело с большим количеством предметов, тогда использование Коллекции может быть намного удобнее в использовании.

Недостаток коллекций

Основные типы данных (т.е. переменные, такие как string, date, long, currency и т.д.) в коллекциях доступны только для чтения. Вы можете добавить или удалить элемент, но не можете изменить его значение. Если вы собираетесь изменять значения в группе элементов, вам нужно будет использовать массив.

Если вы храните объект в коллекции, вы можете изменить значение, так как коллекция хранит ссылку, а не фактический объект.

Коллекция только для чтения

Теперь, когда мы знаем, когда и зачем использовать коллекцию, давайте посмотрим, как ее использовать.

Как создать коллекцию

Вы можете объявить и создать в одной строке, как это делает следующий код:

   ' Объявить и создать
    Dim coll As New Collection

Как видите, вам не нужно указывать размер. После того, как ваша коллекция создана, вы можете легко добавлять в нее элементы.

Вы также можете объявить и затем создать коллекцию, если и когда вам это нужно.

' Объявить
Dim coll As Collection

' Создать Коллекцию
Set coll = New Collection

Незначительная разница между этими методами

Разница между этими методами заключается в том, что для первого всегда создается коллекция. Для второго метода коллекция создается только при достижении строки Set. Таким образом, вы можете установить код для создания коллекции только при соблюдении определенного условия.

' Объявить
    Dim coll As Collection

    ' Создать коллекцию, если файл найден
    If filefound = True Then
        Set coll = New Collection
    Endif

Преимущество использования этого метода минимально. Выделение памяти было важно еще в 1990-х годах, когда память компьютера была ограничена. Если вы не создаете огромное количество коллекций на медленном ПК, вы никогда не заметите никакой выгоды.

Использование Set означает, что коллекция будет вести себя иначе, чем когда вы устанавливаете коллекцию в ничто. Следующий раздел объясняет это.

Удаление всех элементов из коллекции

Коллекция не имеет функции RemoveAll. Однако, чтобы удалить все элементы из коллекции, вы можете просто установить ее в новую коллекцию.

  Set Coll = New Collection.

VBA удалит коллекцию, потому что мы больше не ссылаемся на нее. Когда мы удаляем все предметы, мы обычно хотим использовать коллекцию снова, поэтому мы эффективно убиваем двух зайцев одним выстрелом, используя этот метод.

Sub UdalitKollekciyu()

    Dim coll1 As New Collection
    
    coll1.Add "яблоко"
    coll1.Add "слива"
    
    ' Оригинальная коллекция удалена
    Set coll1 = New Collection

End Sub

Следует помнить одну тонкую вещь: если у нас есть две или более переменных, которые ссылаются на одну и ту же коллекцию, она не будет удалена (см. Очистка памяти в VBA).

В приведенном ниже примере исходные элементы коллекции не удаляются, так как он все еще является ссылкой по coll2.

Sub KollekciyaNeUdalyaetsya()

    Dim coll1 As New Collection, coll2 As Collection
    
    coll1.Add "яблоко"
    coll1.Add "слива"
    
    ' Coll1 и Coll2 оба ссылаются на коллекцию
    Set coll2 = coll1
    
    ' Coll1 теперь ссылается на новую коллекцию
    Set coll1 = New Collection
    
    ' Coll2 относится к оригинальной коллекции - печатает яблоко
    Debug.Print coll2(1)

End Sub

Добавление предметов в коллекцию

Добавить предметы в коллекцию просто. Вы используете свойство add, за которым следует значение, которое вы хотите добавить.

 collFruit.Add "Яблоко"
 collFruit.Add "Слива"

Вы можете иметь любой базовый тип в коллекции, например, Double

collTotals.Add 45.67
collTotals.Add 34.67

Когда вы добавляете элементы таким способом, они добавляются в следующий доступный индекс. В примере с фруктами яблоко добавляется в положение 1, а слива — в положение 2.

Before и After

Вы можете использовать параметры «Before» или «After», чтобы указать, где вы хотите разместить элемент в коллекции. Обратите внимание, что вы не можете использовать оба этих аргумента одновременно.

collFruit.Add "Яблоко"
collFruit.Add "Слива"
' Добавить лимон перед первым пунктом
collFruit.Add "Лимон", Before:=1

После этого кода порядок коллекции выглядит так:

  1. Лимон
  2. Яблоко
  3. Слива
collFruit.Add "Яблоко"
collFruit.Add "Слива"
' Добавьте лимон после первого пункта
collFruit.Add "Лимон", After:=1

После этого кода порядок коллекции выглядит так:

  1. Яблоко
  2. Лимон
  3. Слива

Доступ к элементам коллекции

Для доступа к элементам коллекции вы просто используете индекс. Как мы увидели, индекс — это позиция элемента в коллекции на основе порядка, в котором они были добавлены.

Порядок также можно установить с помощью параметра «Before» или «After».

Sub dostup()

    Dim coll As New Collection

    coll.Add "Яблоко"
    coll.Add "Слива"

    ' Будет напечатано Яблоко
    Debug.Print coll(1)

    ' добавьте апельсин в начало
    coll.Add "Апельсин", Before:=1

    ' будет напечатан Апельсин
    Debug.Print coll(1)

    ' Будет печатать Яблоко, так как оно сейчас в положении 2
    Debug.Print coll(2)

End Sub

Вы также можете использовать свойство элемента для доступа к элементу в коллекции. Это метод по умолчанию для коллекции, поэтому следующие строки кода эквивалентны.

  Debug.Print coll(1)
  Debug.Print coll.Item(1)

Элементы в коллекции доступны только для чтения?

Это очень важный момент. Когда базовый тип данных хранится в коллекции, он доступен только для чтения. Базовый тип данных — это строка, дата, целое число, длина и т.д.

Если вы попытаетесь обновить элемент коллекции, вы получите сообщение об ошибке. Следующий код выдает ошибку «Требуется объект».

Sub NapisatZnachenie()

    Dim coll As New Collection

    coll.Add "Яблоко"

    ' Эта строка вызывает ОШИБКУ
    coll(1) = "Слива"

End Sub

Вы можете изменить объект, который хранится в коллекции

Sub IzmenitObekt()

    Dim coll As New Collection
    Dim o As New Class1
    
    ' Добавить объект в коллекцию
    o.fruit = "Яблоко"
    coll.Add o
    
    ' Изменить фруктовую часть Class1
    coll(1).fruit = "Слива"
    
    ' Печатает Слива
    Debug.Print coll(1).fruit

End Sub

Это может показаться противоречивым поведением, но тому есть веская причина. Любой элемент, добавленный в коллекцию, доступен только для чтения. Однако при добавлении объекта в коллекцию объект не добавляется как элемент. Переменная с адресом памяти объекта добавляется в качестве элемента.

Это происходит незаметно, чтобы вы этого не заметили. Переменная item на самом деле доступна только для чтения, но объект, на который она указывает, — нет.

Все, что вам нужно помнить, это то, что базовые типы данных в коллекции доступны только для чтения. Объекты в коллекции могут быть изменены.

Вы можете прочитать больше об объектах в памяти здесь.

Добавление разных типов

Вы также можете добавлять различные типы предметов в коллекцию.

 collFruit.Add "Яблоко"
 collFruit.Add 45
 collFruit.Add #12/12/2019#

Это редко нужно. В VBA коллекции Sheets содержат листы типа Worksheet и типа Chart. (Чтобы создать лист с диаграммой, просто щелкните правой кнопкой мыши любую диаграмму, выберите «Переместить» и установите переключатель «Новый лист»).

Следующий код отображает тип и имя всех листов в текущей книге. Обратите внимание, что для доступа к другому типу необходимо, чтобы переменная For Each была вариантом, иначе вы получите ошибку.

Sub listi()

    Dim sh As Variant
    For Each sh In ThisWorkbook.Sheets
        ' Тип отображения и название листа
        Debug.Print TypeName(sh), sh.Name
    Next

End Sub

При доступе к различным элементам переменная For Each должна быть вариантом. Если это не так, вы получите ошибку при доступе к другому типу, который вы объявили. Если мы объявим sh в качестве рабочего листа в приведенном выше примере, это приведет к ошибке при попытке доступа к листу типа Chart.

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

Добавление элементов с помощью ключа

Вы также можете добавить элементы, используя ключ, как показано в следующем примере:

collMark.Add Item:=45, Key:="Петр"

Debug.Print "Оценки Петра: ",collMark("Петр")

Я включил имена параметров, чтобы сделать приведенный выше пример понятным. Однако вам не нужно этого делать. Просто помните, что ключ является вторым параметром и должен быть уникальной строкой.

Следующий код показывает второй пример использования ключей.

Sub IspolzovanieKlyuchei()

    Dim collMark As New Collection

    collMark.Add 45, "Петр"
    collMark.Add 67, "Юрий"
    collMark.Add 12, "Ольга"
    collMark.Add 89, "Елена"

    ' Печатать оценку Елены
    Debug.Print collMark("Елена")

    ' Печатать оценку Петра
    Debug.Print collMark("Петр")

End Sub

Использование ключей имеет три преимущества:

  1. Если заказ изменится, ваш код все равно получит доступ к нужному товару
  2. Вы можете напрямую получить доступ к элементу, не читая всю коллекцию
  3. Это может сделать ваш код более читабельны

В коллекции Workbooks VBA доступ к рабочей книге гораздо лучше по ключу (имени), чем по индексу. Порядок зависит от порядка, в котором они были открыты, и поэтому является довольно случайным.

Sub IspolzovanieWorkbook()

    Debug.Print Workbooks("Пример.xlsm").Name

    Debug.Print Workbooks(1).Name

End Sub

Когда использовать ключи

Пример использования ключей: представьте, что у вас есть набор идентификаторов для 10 000 учащихся вместе с их оценками.

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

Вы можете сделать это, добавив 10 000 студентов в коллекцию, используя их идентификатор в качестве ключа. Когда вы читаете удостоверение личности с листа, вы можете получить прямой доступ к оценкам этого студента.

Если вы не используете ключ, вам придется искать по 10 000 идентификаторов для каждого идентификатора в отчете.

Недостаток использования ключей в коллекциях

В коллекциях есть два недостатка ключей

  1. Вы не можете проверить, существует ли Ключ.
  2. Вы не можете обновить значение, хранящееся в ключе

Первый вопрос легко обойти. Следующий код проверяет, существует ли ключ.

Function Exists(coll As Collection, key As String) As Boolean

    On Error Goto EH

    coll.Item key
    
    Exists = True
EH:
End Function

Вы можете использовать это так:

Sub TestExists()

    Dim coll As New Collection
    coll.Add Item:=5, key:="Яблоко"
    coll.Add Item:=8, key:="Слива"
    
    ' Печатает истина
    Debug.Print Exists(coll, "Яблоко")
    ' Печатает ложь
    Debug.Print Exists(coll, "Апельсин")
    ' Печатает истина
    Debug.Print Exists(coll, "Слива")
    
End Sub

Второй вопрос не так легко обойти, если у вас нет хороших знаний в программировании.

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

Доступ ко всем элементам в коллекции

Для доступа ко всем элементам в коллекции вы можете использовать цикл For или цикл For Each. Давайте рассмотри каждый из них.

Использование цикла For

В обычном цикле For вы используете индекс для доступа к каждому элементу. В следующем примере выводится имя всех открытых рабочих книг.

Sub VseWorkbook()

    Dim i As Long
    For i = 1 To Workbooks.Count
        Debug.Print Workbooks(i).Name
    Next i

End Sub

Вы можете видеть, что мы используем диапазон от 1 до Workbooks.Count. Первый элемент всегда находится в первом положении, а последний элемент всегда находится в положении, указанном свойством Count коллекции.

В следующем примере распечатываются все элементы в пользовательской коллекции.

Sub IspolzovanieKollekcii()

    ' Объявить и создать коллекцию
    Dim collFruit As New Collection

    ' Добавить элементы
    collFruit.Add "Яблоко"
    collFruit.Add "Слива"
    collFruit.Add "Груша"

    ' Печать всех элементов
    Dim i As Long
    For i = 1 To collFruit.Count
        Debug.Print collFruit(i)
    Next i

End Sub

Использование цикла For Each

Цикл For Each, который является специализированным циклом, используется для коллекций. Он не использует индекс, а формат показан в следующем примере.

Sub VseWorkbookForEach()

    Dim book As Variant
    For Each book In Workbooks
        Debug.Print book.Name
    Next

End Sub

Формат цикла For:

For i = 1 To Coll.Count
Next

где i — long, Coll — коллекция

Формат цикла For Each:

For Each var In Coll
Next

где var — вариант, а Coll — коллекция

Чтобы получить доступ к каждому элементу:

For: Coll(i)
For Each: Var

В следующем примере показаны циклы для приведенного выше примера пользовательской коллекции.

Sub IspolzovanieOboihCiklov()

    ' Объявить и создать коллекцию
    Dim collFruit As New Collection

    ' Добавить элементы
    collFruit.Add "Яблоко"
    collFruit.Add "Слива"
    collFruit.Add "Груша"

    ' Печать всех элементов, используя For
    Dim i As Long
    For i = 1 To collFruit.Count
        Debug.Print collFruit(i)
    Next i

    ' Печать всех элементов, используя For Each
    Dim fruit As Variant
    For Each fruit In collFruit
        Debug.Print fruit
    Next fruit

End Sub

For Each против For

Важно понимать разницу между двумя циклами.

Цикл For Each

  • быстрее
  • аккуратнее писать
  • имеет только один заказ — от низкого индекса до высокого

Цикл For

  • медленнее
  • не так аккуратен
  • можно получить доступ в другом порядке

Давайте сравним циклы по каждому из этих атрибутов

Скорость

For Each считается быстрее, чем цикл For. В настоящее время это проблема, только если у вас большая коллекция и / или медленный компьютер/сеть.

Аккуратнее

Цикл For Each аккуратнее писать, особенно если вы используете вложенные циклы. Сравните следующие циклы. Оба печатают названия всех рабочих листов в открытых рабочих книгах.

Sub PechatatNazvaniyaFor()

    ' Напечатайте названия рабочих листов из всех открытых рабочих книг
    Dim i As Long, j As Long
    For i = 1 To Workbooks.Count
        For j = 1 To Workbooks(i).Worksheets.Count
            Debug.Print Workbooks(i).Name, Workbooks(i).Worksheets(j).Name
        Next j
    Next i

End Sub

Sub PechatatNazvaniyaForEach()

    ' Напечатайте названия рабочих листов из всех открытых рабочих книг
    Dim bk As Workbook, sh As Worksheet
    For Each bk In Workbooks
        For Each sh In bk.Worksheets
            Debug.Print bk.Name, sh.Name
        Next sh
    Next bk

End Sub

Цикл For Each гораздо удобнее для написания и менее подвержен ошибкам.

Порядок

Порядок цикла For Each всегда от самого низкого индекса до самого высокого. Если вы хотите получить другой заказ, вам нужно использовать цикл For. Порядок цикла For можно изменить. Вы можете прочитать предметы в обратном порядке. Вы можете прочитать раздел предметов или вы можете прочитать каждый второй элемент.

Sub ChitatSpravaNalevo()

' Просмотрите листы справа налево
Dim i As Long
For i = ThisWorkbook.Worksheets.Count To 1 Step -1
Debug.Print ThisWorkbook.Worksheets(i).Name
Next i

' Пройдите первые 3 листа
For i = 1 To 3
Debug.Print ThisWorkbook.Worksheets(i).Name
Next i

' Пройдите каждый второй лист
For i = 1 To ThisWorkbook.Worksheets.Count Step 2
Debug.Print ThisWorkbook.Worksheets(i).Name
Next i

End Sub

Цикл For дает больше гибкости, но реальность такова, что большую часть времени базовый порядок — это все, что вам нужно.

Сортировка коллекции

Для коллекции VBA нет встроенной сортировки. Однако мы можем использовать QuickSort.

Sub QuickSort(coll As Collection, first As Long, last As Long)
  
  Dim vCentreVal As Variant, vTemp As Variant
  
  Dim lTempLow As Long
  Dim lTempHi As Long
  lTempLow = first
  lTempHi = last
  
  vCentreVal = coll((first + last)  2)
  Do While lTempLow <= lTempHi
  
    Do While coll(lTempLow) < vCentreVal And lTempLow < last
      lTempLow = lTempLow + 1
    Loop
    
    Do While vCentreVal < coll(lTempHi) And lTempHi > first
      lTempHi = lTempHi - 1
    Loop
    
    If lTempLow <= lTempHi Then
    
      ' Поменять значения
      vTemp = coll(lTempLow)
      
      coll.Add coll(lTempHi), After:=lTempLow
      coll.Remove lTempLow
      
      coll.Add vTemp, Before:=lTempHi
      coll.Remove lTempHi + 1
      
      ' Перейти к следующим позициям
      lTempLow = lTempLow + 1
      lTempHi = lTempHi - 1
      
    End If
    
  Loop
  
  If first < lTempHi Then QuickSort coll, first, lTempHi
  If lTempLow < last Then QuickSort coll, lTempLow, last
  
End Sub

Вы можете использовать это так:

Sub TestSort()

    Dim coll As New Collection
    coll.Add "Москва"
    coll.Add "Тверь"
    coll.Add "Пенза"
    coll.Add "Новосибирск"
    
    QuickSort coll, 1, coll.Count
    
    Dim v As Variant
    For Each v In coll
        Debug.Print v
    Next
    
End Sub

Использование коллекций с функциями и подпрограммами

Использовать коллекцию в качестве параметра или возвращаемого значения очень легко. Мы рассмотрим их по очереди.

Передача Коллекции в Sub / Function

Это просто передать коллекцию в функцию или подпункт. Она передается как любой параметр, как показано в следующем примере кода.

Sub IspolzovatColl()

    ' Создать коллекцию
    Dim coll As New Collection

    ' Добавить элементы
    coll.Add "Яблоко"
    coll.Add "Апельсин"

    ' Перейти на sub
    PrintColl coll

End Sub

' Sub принимает коллекцию в качестве аргумента
Sub PrintColl(ByRef coll As Collection)

    Dim item As Variant
    For Each item In coll
        Debug.Print item
    Next

End Sub

Вы можете увидеть, насколько полезен вспомогательный PrintColl в примере. На нем будут напечатаны все элементы ЛЮБОЙ коллекции. Размер или тип элемента не имеет значения. Это показывает, насколько гибкими должны быть коллекции.

Передача ByVal против ByRef

Здесь следует помнить одну тонкую вещь: передача по значению (By Val) и передача по ссылке (ByRef) немного отличаются.

Для простой передачи переменной значение означает, что копия создана. Это означает, что если функция / подчиненный элемент изменится, значение не изменится, когда вы вернетесь к вызывающей процедуре.

В следующем примере мы передаем итоговое значение, используя ByVal и ByRef. Вы увидите, что после того, как мы передаем использование ByRef, значение изменилось в вызывающей процедуре.

Sub TipiPeredachi()

    Dim total As Long
    total = 100

    PassByValue total
    ' Печатает 100
    Debug.Print total

    PassByReference total
    ' Печатает 555
    Debug.Print total

End Sub

Sub PassByValue(ByVal total As Long)
    ' значение изменилось только в этом sub
    total = 555
End Sub

Sub PassByReference(ByRef total As Long)
    ' значение также изменилось за пределами этого sub
    total = 555
End Sub

Использование ByVal и ByRef с коллекцией немного отличается. Если вы добавляете или удаляете элемент, коллекция в исходном вызывающем абоненте также будет изменена. Таким образом, Subs в следующем примере удалят первый элемент из исходной коллекции.

Sub UdalitByRef(ByRef coll As Collection)
    coll.Remove 1
End Sub

Sub RemoveByVal(ByVal coll As Collection)
    coll.Remove 1
End Sub

Причина этого заключается в том, что переменная Collection содержит указатель. Это означает, что он содержит адрес коллекции, а не фактическую коллекцию. Поэтому, когда вы добавляете или удаляете элемент, вы меняете то, на что указывает указатель, а не сам указатель. Однако, если вы измените указатель, он будет изменен за пределами подпрограммы.

Вам не нужно беспокоиться об указателях. Все, что вам нужно знать, это то, как это влияет на поведение передачи параметра. Если для параметра коллекции ничего не задано, поведение зависит от того, использовали ли вы ByRef или ByVal.

  • Использование ByRef сбросит исходную коллекцию
  • Использование ByVal не изменит оригинальную коллекцию
' Очистит исходную коллекцию
Sub PassByRef(ByRef coll As Collection)
    Set coll = Nothing
End Sub

' Не изменит исходную коллекцию
Sub PassByVal(ByVal coll As Collection)
    Set coll = Nothing
End Sub

Возврат коллекции из функции

Возврат коллекции из функции — это то же самое, что вернуть любой объект. Вам нужно использовать ключевое слово Set. В следующем примере вы увидите, как вернуть коллекцию.

Sub OtchetOFruktah()
    ' ПРИМЕЧАНИЕ. Мы не используем ключевое слово New для создания 'коллекции.
    ' Коллекция создается в функции CreateCollection.
    Dim coll As Collection

    ' получить coll от функции CreateCollection
    Set coll = CreateCollection

    ' сделать что-то с coll здесь

End Sub

Function CreateCollection() As Collection

    Dim coll As New Collection

    coll.Add "Слива"
    coll.Add "Груша"

    ' Возврат коллекции
    Set CreateCollection = coll

End Function

Примечание: вы не используете ключевое слово New при объявлении коллекции в подпункте OtchetOFruktah(). Потому что коллекция создается в CreateCollection (). Когда вы возвращаете коллекцию, вы просто назначаете переменную коллекции, указывающую на эту коллекцию.

Заключение

Коллекции — очень полезная часть VBA. Их гораздо проще использовать, чем массивы, и они очень полезны, когда вы много добавляете и удаляете элементы. У них есть только четыре свойства:
Add, Remove, Count и Item . Поэтому они очень просты в освоении.

 Основные пункты этой статьи:

  1. Коллекции — это способ хранения группы элементов вместе.
  2. VBA имеет свои собственные коллекции, такие как
    Workbooks, Worksheets и Cells .
  3. Элементы не обязательно должны быть одного типа, но обычно одного. Коллекция VBA Sheets может содержать как листы, так и листы диаграмм.
  4. Коллекция позволяет легко выполнять одну и ту же задачу для нескольких элементов, например, распечатать все значения.
  5. Коллекции похожи на массивы, поскольку в них хранятся группы похожих элементов.
  6. Коллекции лучше при добавлении и удалении большого количества элементов.
  7. Коллекции проще в использовании, чем массивы.
  8. Массивы более полезны, когда количество элементов фиксировано.
  9. Массивы более эффективны при чтении и записи в ячейки или из них.
  10. Базовые типы данных (т.е. не-объекты) в коллекции доступны только для чтения, а массивы — для чтения / записи.
  11. Вы можете создать коллекцию, используя только Dim или Dim с помощью Set.
  12. Вы можете удалить всю коллекцию, установив для нее значение Nothing. Но зависит от того, как она была создана.
  13. Вы можете добавлять элементы в определенную позицию в коллекции, используя аргументы «Before» и «After» с помощью функции Add.
  14. Вы можете использовать ключи с коллекцией для прямого доступа к элементу. Коллекции не имеют хорошей поддержки ключей, поэтому обычно лучше использовать коллекцию Dictionary, когда вам нужно использовать ключи.
  15. Вы можете использовать циклы For и For Each для доступа ко всем элементам в коллекции. Цикл For Each более эффективен, но позволяет просматривать коллекцию только в одном порядке.
  16. Вы можете легко передать коллекцию в качестве аргумента в функцию или подпрограмму.
  17. Вы можете легко вернуть коллекцию из функции.

Коллекции в макросах ExcelКоллекции (Collection) элементов Excel – это объект, представляющий из себя упорядоченный набор данных, на который можно ссылаться как на одно целое.
Отдельные элементы коллекции являются также объектами. Элементы коллекции никак между собой не связаны и могут иметь разные типы данных.
Примеры готовых коллекций в VBA: Workbooks, Worksheets, Range и Cells.

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

Коллекции и массивы элементов в Excel

Коллекции и массивы используются для группировки переменных в Excel. Они оба хранят набор похожих данных, например список названий фруктов или фамилий сотрудников. Использование коллекции или массива позволяет работать с большим количеством данных в Excel , а также быстро и легко получать нужный результат.

Например, есть 100 учеников, которые получили оценки по предметам в году. Нужно задать переменные по ученикам, причем все они должны быть использованы по отдельности. Для того, чтобы не объявлять каждый раз переменную, можно использовать коллекцию или массив. Использование цикла с коллекцией или массивами означает, что вам нужна только одна строка для добавления или чтения значений.

 ' Создать массив правильного размера 
Dim coll() As Long
ReDim coll(1 To StudentCount)
' Создать коллекцию Dim coll As New Collection

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

  • Количество данных, используемых в массиве или коллекции

Основное отличие состоит в том, что в массиве размер устанавливается один раз до того, как начнете добавлять элементы. Массивы используются, когда размер фиксирован.
Например, есть лист оценок учеников, на котором каждый ученик располагается на новой строке. В этом случае можно  посчитать количество строк, чтобы получить количество студентов. 

Рассмотрим другой пример, где мы не знаем количество заранее. Например, нужно сделать выборку по определенным предметам, а также в процессе обучения ученики могут быть добавлены или удалены из списка. Таким образом, данные по ученикам изменяются, поэтому вы не знаете, какой размер массива создать. 
Вы можете создать массив максимально возможного размера. Проблема в том, что у вас будет много пустых переменных, с которыми придется что-то делать. Это очень неэффективно. Так что для этого примера лучше использовать коллекцию. Коллекция используется, когда размер часто изменяется.

Когда вы добавляете или удаляете элемент в коллекцию, макрос выполняет все изменения размера сам. Все, что вам нужно сделать, это добавить элемент или удалить его.

 ' Объявить
    Dim coll As New Collection

    ' Добавить элемент - VBA следит за изменением размера
    coll.Add "Яблоко"
    coll.Add "Слива"

    ' удалить элемент - VBA следит за изменением размера
    coll.Remove 1
  • Использование данных в массивах и коллекциях

Основные типы данных (переменные, такие как string, date, long, currency и т.д.) в коллекциях доступны только для чтения. Вы можете добавить или удалить элемент, но не можете изменить его значение. Если вы собираетесь изменять значения в группе элементов, Вам нужно будет использовать массив. Коллекция только для чтения.

Создание коллекции

Вы можете объявить и создать коллекцию в одной строке:

    Dim coll As New Collection   

Вы также можете объявить и затем создать коллекцию, если и когда вам это нужно.

    Dim coll As Collection 'Объявить
Set coll = New Collection 'Создать

Разница между этими методами заключается в следующем:
— в первом случае коллекция создается всегда;
— во втором случае коллекция создается только при соблюдении определенного условия (достижении строки Set). Например, If filefound = True Then Set coll = New Collection

Удаление всех элементов из коллекции

Чтобы удалить все элементы из коллекции, вы можете просто установить ее в новую коллекцию: 
Set Coll = New Collection

Важно: если у нас есть две или более переменных, которые ссылаются на одну и ту же коллекцию, она не будет удалена (см. Очистка памяти в VBA).

Добавление элементов в коллекцию

С помощью свойства add, за которым следует добавляемое значение:

   coll.Add "Яблоко" 
coll.Add "Слива"

Каждый следующий элемент добавляется в следующий доступный индекс. В примере с фруктами яблоко добавляется в положение 1, а слива — в положение 2.

Вы можете использовать параметры «Before» или «After», чтобы указать, где вы хотите разместить элемент в коллекции:

  • coll.Add «Лимон», Before:=1 ‘Добавить лимон перед первым пунктом.
    После этого кода порядок коллекции выглядит так: 1.Лимон 2.Яблоко 3.Слива
  • coll.Add «Лимон», After:=1 ‘Добавьте лимон после первого пункта
    После этого кода порядок коллекции выглядит так: 1.Яблоко 2. Лимон 3.Слива

Использование ключей для коллекции элементов Excel

Вы также можете добавить элементы, используя ключ:
collMark.Add Item:=45, Key:=»Петр»
Использование ключа позволяет получить доступ к элементу не зная его индекса, а просто задав его ключ. Даже если изменится индекс элемента (его порядковый номер), то по ключу мы все равно получим нужный элемент.
Важно: каждый ключ должен быть уникальным.

Преимущество использование ключей в том, что можно получить значение элемента коллекции напрямую и не искать его среди других элементов.
Недостатком использования ключей является то, что нельзя проверить, существует ли ключ.

Синтаксис добавления элементов в коллекцию

Collection.Add Элемент, [Ключ], [До], [После], где

  1. Collection – переменная, которая обозначает имя коллекции.
  2. Элемент (item) – обязательный аргумент, представляющий выражение любого типа, возвращающее элемент, который необходимо добавить в коллекцию.
  3. Ключ (key) – необязательный аргумент, представляющий строковое выражение, задающее уникальный ключ, который может использоваться вместо индекса позиции для доступа к элементу коллекции.
  4. До* (before) – необязательный аргумент, указывающий на позицию существующего элемента в коллекции, перед которым будет добавлен новый элемент.
  5. После* (after) – необязательный аргумент, указывающий на позицию существующего элемента в коллекции, после которого будет добавлен новый элемент.

Аргументы «До» и «После» не могут применяться одновременно. Если аргументу «До» или «После» присвоено числовое значение, оно должно быть в пределах диапазона от 1 до значения свойства Collection.Count. Если это строка, она должна соответствовать одному из ключей существующих в коллекции элементов.

Доступ к элементам коллекции

Для доступа к элементам коллекции используется индекс  — позиция элемента в коллекции на основе порядка, в котором они были добавлены. 
Порядок также можно установить с помощью параметра «Before» или «After».

Чтобы напечатать элемент используется команда Debug.Print:
Debug.Print coll(1) или Debug.Print coll.Item(1)
Debug.Print coll(«Петр»)
‘при использовании ключей

Для доступа ко всем элементам в коллекции вы можете использовать цикл For или цикл For Each. 

Использование цикла For

    For i = 1 To coll.Count
        Debug.Print coll(i)
    Next i

Использование цикла For Each

    Dim fruit As Variant
    For Each fruit In coll
        Debug.Print fruit
    Next fruit

Понравилась статья? Поделить с друзьями:
  • Vba excel количество файлов в папке
  • Vba excel массив as range
  • Vba excel количество строк диапазоне
  • Vba excel максимальный столбец
  • Vba excel количество строк диапазона