2012-07-02 1 views
32

Как и в случае с this issue, при использовании объекта Scripting.Dictionary в VBA результат приведенного ниже кода является неожиданным.Looping через Scripting.Dictionary с использованием индекса/номера позиции

Option Explicit 

Sub test() 

    Dim d As Variant 
    Dim i As Integer 
    Dim s As String 
    Set d = CreateObject("Scripting.Dictionary") 

    d.Add "a", "a" 
    Debug.Print d.Count ' Prints '1' as expected 

    For i = 1 To d.Count 
     s = d.Item(i) 
     Debug.Print s ' Prints ' ' (null) instead of 'a' 
    Next i 

    Debug.Print d.Count ' Prints '2' instead of '1' 

End Sub 

Используя индекс с отсчетом от нуля, то же результат достигается:

For i = 0 To d.Count - 1 
    s = d.Item(i) 
    Debug.Print s 
Next i 

Наблюдая объект, я на самом деле можно увидеть, что у него есть два элемента, ключ для вновь добавленного 1, как добавлено от i. Если я увеличиваю этот цикл до более высокого числа, то количество элементов в словаре увеличивается один раз для каждого цикла.

Я тестировал это в Office/VBA 2003, 2010 и 2013 годах. Все проявляют одинаковое поведение, и я ожидаю, что другие версии (2007) также будут.

я могу обойти эту проблему с другими методами сквозных, но это застало меня врасплох, когда я пытался хранить объекты и было получение объекта ожидаемой ошибки на s = d.Item(i) линии.

Для записи, я знаю, что я могу сделать что-то вроде этого:

For Each v In d.Keys 
    Set o = d.item(v) 
Next v 

Но я более любопытно, почему я не могу показаться, чтобы перебирать пункты по номеру.

+2

@assylias Я вижу это в документации '.Item (Key)', но я также вижу '.Key (Key)', хотя я не уверен, как использовать метод 'Key' ... Есть ли способ итерации по номеру позиции? – Gaffi

ответ

30

Согласно the documentation of the Item property:

Устанавливает или возвращает элемент для указанного ключа в словарь объекта.

В вашем случае, вы не имеете элемент с ключом 1 таким образом:

s = d.Item(i) 

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

Словарь также имеет Items method, которая позволяет зацикливание по индексам:

a = d.Items 
For i = 0 To d.Count - 1 
    s = a(i) 
Next i 
+2

Я следую этому, но не существует способа, чтобы итерации/чтения по индексу/номеру позиции? * Кажется, это глупая реализация, если вы спросите меня. ;) * – Gaffi

+0

И с этой информацией я изменил вопрос, чтобы не включать формулировку «ошибка». – Gaffi

+0

@ Gaffi См. Мое редактирование – assylias

-1

Это ноль на основе так что ваша петля должна быть от 0 до d.Count - 1

+0

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

+0

Хорошо, что вы только что приняли решение на основе нуля выше, одно немного запутано. И этот ответ, который вы приняли, также говорит, что использование индекса, который не существует, добавляет его, становится еще более запутанным. Все еще берет все виды а? –

+0

Ваш ответ не был ошибочным; это просто не решило мою конкретную проблему. Для записи я не дал -1. Проблема в том, что использование '.Item (index)' неверно. Вместо этого использование '.Items' (note 's') для создания массива ключей позволит вам делать то, что я искал. – Gaffi

32

Добавление ответа assylias'S - assylias показывает нам d.Items это метод, который возвращает массив. Зная, что нам не нужен массив вариантов a (i) [см. Ниже описание]. Нам просто нужно использовать правильный синтаксис массива.

For i = 0 To d.Count - 1 
    s = d.Items()(i) 
    Debug.Print s 
Next i() 

КЛЮЧИ работает таким же образом

For i = 0 To d.Count - 1 
    Debug.Print d.Keys()(i), d.Items()(i) 
Next i 

Этот синтаксис также полезен для функции SPLIT, которые могут помочь сделать это более ясным. SPLIT также возвращает массив с нижними границами в 0. Таким образом, следующие отпечатки «C».

Debug.Print Split("A,B,C,D", ",")(2) 

SPLIT - это функция.Его параметры находятся в первом наборе круглых скобок. Методы и функции всегда используют первый набор скобок для параметров, даже если параметры не нужны. В примере SPLIT возвращает массив {"A", "B", "C", "D"}. Поскольку он возвращает массив, мы можем использовать второй набор круглых скобок, чтобы идентифицировать элемент в возвращаемом массиве так же, как и любой массив.

Caveat: Этот короткий синтаксис не может быть столь же эффективным, как используя массив вариант А() при переборе через весь словарь, так как более короткий синтаксис вызывает метод Items словаря с каждой итерации. Более короткий синтаксис лучше всего выщипывать один элемент по номеру из словаря.