Цикл по Scripting.Dictionary с использованием индекса / номера элемента

34

Подобно этой проблеме, при использовании объекта Scripting.Dictionary в VBA результат кода ниже неожиданен.

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) также будут.

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

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

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

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

  • 2
    @assylias Я вижу это в документации .Item(Key) но также вижу .Key(Key) , хотя я не уверен, как использовать метод Key ... Есть ли способ выполнить итерацию по номеру элемента?
Теги:
dictionary

4 ответа

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

В соответствии с документацией свойства Item:

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

В вашем случае у вас нет элемента с ключом 1:

s = d.Item(i)

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

В словаре также есть метод Items, который позволяет перебирать индексы:

a = d.Items
For i = 0 To d.Count - 1
    s = a(i)
Next i
  • 2
    Я следую этому, но нет ли способа перебрать / прочитать по индексу / номеру элемента? Похоже, глупая реализация, если вы спросите меня. ;-)
  • 0
    И с этой информацией я изменил вопрос, чтобы не включать формулировку «ошибка».
Показать ещё 4 комментария
34

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

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

KEYS работает одинаково

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. Этот более короткий синтаксис может быть не столь эффективным, как использование альтернативного массива a() при итерации по всему словарю, поскольку более короткий синтаксис вызывает метод словарных позиций с каждой итерацией. Более короткий синтаксис лучше всего выщипывать один элемент по номеру из словаря.

0

Использование d.Keys()(i) - очень плохая идея, потому что при каждом вызове он будет заново создавать новый массив (у вас будет значительное снижение скорости).

Вот аналог Scripting.Dictionary под названием "Хэш-таблица" класса от @"The Trick", который поддерживает такой перечислитель: http://www.cyberforum.ru/blogs/354370/blog2905.html

Dim oDict As clsTrickHashTable

Sub aaa()
    Set oDict = New clsTrickHashTable

    oDict.Add "a", "aaa"
    oDict.Add "b", "bbb"

    For i = 0 To oDict.Count - 1
        Debug.Print oDict.Keys(i) & " - " & oDict.Items(i)
    Next
End Sub
-2

Он основан на нуле, поэтому ваш цикл должен быть от 0 до d.Count - 1

  • 0
    Я не верю, что это правильное решение. Когда я начал писать код, я также подумал, что он основан на нуле, но изменение структуры цикла не решило проблему - он по-прежнему добавляет новый элемент.
  • 0
    Хорошо, учитывая, что вы только что приняли решение на основе нуля выше, одно немного запутано. И тот ответ, который вы приняли, также говорит о том, что использование индекса, которого нет, добавляет один, еще больше запутываетесь. Все еще принимает все виды а?
Показать ещё 2 комментария

Ещё вопросы

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