Страницы: 1
RSS
VBA: несколько вопросов по работе со словарем
 
Добрый день. Помогите понять, плиз. Про словария я почитал и общий их смысл вроде понимаю
Есть код (содержится в примере) massInDic1 он позволяет добавлять в словаря одинаковые на первый взгляд значения
Во втором коде massInDic2 это не возможно. Почему так происходит?
И попутный вопрос. В окне Locals, раскрывая словарь я вижу что в Items записывается значения Key и не вижу нигде то что я записал в Items. Почему?
Можно ли напрямую обратится обратиться к элементу (ключ, объект) словаря? Например какнибудь так
a =  dicTest(item1)
b = dicTest(Key1)
 
1. В первом макросе у вас ключ iKey - объект типа Range. Все Range в примере разные, независимо от их значений, поэтому добавляются внешне схожие ключи. Во втором макросе ключ словаря имеет тип данных Double, это видно тут: http://joxi.ru/4zANP1ZT1N6jm9.png Значения совпадают, следовательно, повторно не записываются.
Соответственно в первом макросе сделайте ключом iKey.Value и будет счастье.
2. Особенности отображения. Сделайте Watch по dicTest.Items
3. dicTest(ключ) - можно. dicTest(значение ключа) - нет (вообще непонятно, что вы хотите получить в данном выражении? А если одинаковое значение у разных ключей?)
Код
Sub qqqqq()
Dim dicTest As Object
Set dicTest = CreateObject("Scripting.Dictionary")
dicTest.Add Key:="ключ1", Item:="Item_один"
Debug.Print dicTest("ключ1")
s = "ключ1"
Debug.Print dicTest(s)
End Sub
F1 творит чудеса
 
Максим Зеленский, спасибо за разъяснения
по п.3 тоже не совсем понял. и поясню что хочу
у нас, если брать ваш код есть словарь из одного ключа и его объекта
перед добавлением нового ключа мы проверяем есть ли такой ключ.
если ключ есть мы извлекаем объект ключа и сравниваем его с объектом добавляемого ключа - для этого и спрашивал про возможность обратиться к Item ключа как к переменной.
дальше в зависимости от результата сравнения мы или перезаписываем ключ с новым значением объекта или отставляем как есть.
 
Цитата
jfd написал:
перед добавлением нового ключа мы проверяем есть ли такой ключ.
если ключ есть мы извлекаем объект ключа и сравниваем его с объектом добавляемого ключа
Предположим, в словаре уже есть ключ "Вася"
Мы хотим, если такой ключ уже есть и ему соответствует значение "рыжий", заменить значение на "брюнет".
Проверка наличия ключа в словаре:
Код
If dicTest.Exists("Вася") Then
    If dicTest("Вася") = "рыжий" Then dicTest("Вася") = "брюнет"
Else
    dicTest.Add Key:="Вася", Item:="брюнет"
End If

Можно проще, без проверки, есть ли ключ "Вася" в словаре и если есть, какое у него значение:
Код
dicTest.Item("Вася")="брюнет"

В этом случае если ключа "Вася" в словаре нет, он создастся с присвоением значения "брюнет". Если ключ есть, его значение перезапишется на "брюнет" (вне зависимости от того, каким оно было ранее, "рыжий" или "блондин").
Так, например, можно устраивать счетчик повторений ключа в каком-то массиве:
Код
dicTest.Item("Вася")=dicTest.Item("Вася")+1

Обратите внимание, что запись dicTest.Item(ключ) использует именно ключ в качестве аргумента. Не его порядковый номер, не присвоенное ключу значение, а сам ключ.
Изменено: Максим Зеленский - 22.07.2015 18:01:43
F1 творит чудеса
 
Максим Зеленский, я правильно понимаю что обращаться к ключам словаря через переменную нельзя
например искать в словаре ключ равный значению переменной?
 
Цитата
jfd написал: искать в словаре ключ равный значению переменной?
jfd, во втором сообщении я привел пример с переменной. Например, если s - это переменная, то запись dicTest(s) как раз и обозначает поиск в словаре ключа, чье значение равно значению переменной s.
Главное, чтобы переменная имела тот же тип, что и ключ словаря.
Только еще обратите внимание - если вы обратитесь к словарю, например, так: Debug.Print dicTest("Вася"), и при этом в словаре такого ключа еще не было, то в словаре создастся запись с ключом "Вася" и соответствующим пустым значением. Так что во избежание случайных добавлений лучше делать проверку на существование.

Похоже, вы не до конца поняли, как работает словарь. Прямая аналогия - словарь. Например, толковый словарь Ожегова. Вы хотите узнать значение слова "рыжий", открываете словарь и находите там слово "рыжий" - получаете толкование. Вторая аналогия: Словарь - это как шкаф с ящиками. Каждый ящик - ключ. А вот внутри что - это уже другой вопрос.
F1 творит чудеса
 
Так и не смог сам разобраться.
Вы не могли бы привести пример кода который в словаре сравнит значения из столбца А и по содержимому В и С выберет одно с наибольшим значением даты?
Заранее спасибо. И за предыдущие разъяснения тоже спасибо
 
Цитата
jfd написал: по содержимому В и С выберет одно с наибольшим значением даты?
Это нужно делать сразу при заполнении словаря - если итем меньше чем очередная дата, то заменяем.
Пример кода с васями выше уже есть - т.е. проверяем наличие, если есть - то сравниваем значения.
Что вообще хотите сделать, в чём задача?
Изменено: Hugo - 29.07.2015 11:52:35
 
Hugo,проблема в том что я не понимаю как в коде реализовать сравнение объектов ключа.
как проверку при добавлении сделать понимаю - работает, а как обратится к объекту ключа - нет

Код
Sub massInDic1()

Dim objArr(2)
Dim dicTest As Object
Set dicTest = CreateObject("Scripting.Dictionary")

For Each iKey In Range("A2:A16")
    objArr(1) = iKey.Offset(0, 1)
    objArr(2) = iKey.Offset(0, 2)
    
     With dicTest
      .Add Key:=iKey.Value, Item:=objArr(1) & " " & objArr(2)
     End With
    
    Debug.Print iKey & " " & objArr(1) & " " & objArr(2)
Next
rrr = Cells(4, 1).Value 'в реальном коде тут будет перебор For Each ... Next
If dicTest.Exists(rrr) Then MsgBox "TRUE"
  'вот тут по идее надо выяснить порядковый номер ключа и обратиться к его объекту для сравнения
End Sub
 
Порядковый номер ключа не нужен. Да их и нет физически - помнится что microsoft даже не обещал стабильную позицию ключа в словаре.
Код
If dicTest.Exists(rrr) Then 
if dictest.item(rrr) < proverjaemajadata then dictest.item(rrr) = proverjaemajadata 
else
dictest.item(rrr) = proverjaemajadata 
end if
вот как-то так будут в словаре только максимальные даты у каждого ключа.
А вообще думаю сработает вообще проще:
Код
if dictest.item(rrr) < proverjaemajadata then dictest.item(rrr) = proverjaemajadata 
т.к. дата кажется всегда больше пустого значения... Хотя нужно проверить, не помню.
 
Hugo,а если я записал в Итем одномерный массив из 2-х элементов, который имеет примерно такой вид "21.06.2014 19:01:4|29.12.2014 08:43:55" то мне надо будет разбить его с помощью текстовых функций на 2е переменные в отдельном цикле и сравнить по нужной логике с rrr.offset (0,1) и   rrr.offset(0,2)? а потом уже решать перезаписывать Итем или нет?
 
Да, можно записать массив (ну или строку, которую бить в массив).
Если пишите массив - то его сперва нужно извлечь в временный массив, перебрать-изменить, записать назад.
Можно записать коллекцию, с ней чуть проще.
 
Hugo, спасибо. где-то вдалеке, на горизонте, забрезжил просвет   :D
 
Вот когда-то писал:
Код
словарь с коллекцией

Схематично так:


Sub PereborFailov() 'коллекция в словаре
    Dim a, i&, t$, Dic As Object
    Dim el, col
    
    a = Range("C3", Cells(Rows.Count, "A").End(xlUp)).Value
    Set Dic = CreateObject("Scripting.Dictionary")
    With Dic
        .CompareMode = 1
        For i = 1 To UBound(a)
            t = a(i, 1)
            If Not .exists(t) Then .Add t, New Collection
            .Item(t).Add a(i, 2) & "|" & a(i, 3) & "|" & i
        Next
    End With
    
    For Each el In Dic.keys
        Debug.Print "Открываем файл " & el
        For Each col In Dic.Item(el)
            Debug.Print "Ищем данные " & col
        Next
        Debug.Print "Закрываем файл " & el
    Next

End Sub

Sub PereborFailov2() ' словарь в словаре
    Dim a, i&, t$, Dic As Object, Dic2 As Object
    Dim el, col
    
    a = Range("C3", Cells(Rows.Count, "A").End(xlUp)).Value
    Set Dic = CreateObject("Scripting.Dictionary")
    With Dic
        .CompareMode = 1
        For i = 1 To UBound(a)
            t = a(i, 1)
            If Not .exists(t) Then .Add t, CreateObject("Scripting.Dictionary")
            .Item(t).Item(a(i, 2) & "|" & a(i, 3) & "|" & i) = 0&
            
        Next
    End With
    
    For Each el In Dic.keys
        Debug.Print "Открываем файл " & el
        Set Dic2 = Dic.Item(el)
        For Each col In Dic2.keys
            Debug.Print "Ищем данные " & col '& "|" & Dic2.Item(col)
        Next
        Debug.Print "Закрываем файл " & el
    Next

End Sub
 
Тут кусочек работы с массивом в итем:
Код
        If Not oDict.Exists(temp) Then
            ReDim b(1 To 2)
            b(1) = a(i, 3): b(2) = a(i, 7)
            oDict.Add temp, b
        Else
            b = oDict.Item(temp)
            b(1) = b(1) + a(i, 3): b(2) = b(2) + a(i, 7)
            oDict.Item(temp) = b
        End If

 
Hugo, даже не представлял что можно в Итеме хранить массив поэлементно, а не одной строкой :)
спасибо. последний пример очень полезен оказался для понимания
 
Цитата
jfd написал:
в Итем одномерный массив из 2-х элементов, который имеет примерно такой вид "21.06.2014 19:01:4|29.12.2014 08:43:55" то мне надо будет разбить его с помощью текстовых функций на 2е переменные в отдельном цикле и сравнить по нужной логике с rrr.offset (0,1) и   rrr.offset(0,2)?
Еще вариант, без извлечения в промежуточный массив - обращаемся к элементам массива напрямую в словаре:
Код
If Not oDict.Exists(temp) Then
            ReDim b(1 To 2)
            b(1) = a(i, 3): b(2) = a(i, 7)
            oDict.Add temp, b
        Else
            oDict(temp)(1) = oDict(temp)(1) + a(i, 3)
            oDict(temp)(2) = oDict(temp)(2) + a(i, 7)
        End If
F1 творит чудеса
 
А вот не работает так...
 
Ыть... и правда не работает... а почему я решил, что будет работать? :(

Но, по крайней мере, извлечь только один элемент массива точно можно :) Мало ли зачем может понадобиться...
Код
Sub fff()
Dim ddd As Object, a(1 To 2), temp
Set ddd = CreateObject("scripting.dictionary")
temp = 0
a(1) = 1
a(2) = 2
ddd(temp) = a
temp = ddd(temp)(1) + 2
Debug.Print temp
End Sub
F1 творит чудеса
 
Здравствуйте! Максим, а всё-таки правда, как изменить какой-нибудь i-ый элемент одномерного массива, который хранится в словаре под каким-то ключом? Считать массив в переменную, изменить её и опять записать в словарь? Или можно напрямую без временной переменной?
Изменено: borro - 28.01.2017 13:30:06
желаю всем счастья
 
Цитата
borro написал:
Или можно напрямую без временной переменной?
Нельзя
 
Спасибо, Михаил
желаю всем счастья
 
Я говорил Вам про другой подход (в другой теме) - храните в словаре индексы массива, а не сам массив. Тогда без проблем можно
Цитата
borro написал:
изменить какой-нибудь i-ый элемент одномерного массива
Изменено: Hugo - 31.01.2017 14:05:29
 
Спасибо, Hugo. Простите, я Вас не понял ни тогда, ни сейчас. Мне видимо на примере надо. В принципе задача решена, можно не тратить Вашего времени.
желаю всем счастья
 
Цитата
borro написал:
Мне видимо на примере надо
- это прямым текстом написано в правилах форума. Поэтому я в общем и не пытаюсь всё разжевать...
Страницы: 1
Читают тему
Наверх