O VBA possui estrutura de dicionário? Como matriz de valores de chave <>?
O VBA possui estrutura de dicionário? Como matriz de valores de chave <>?
Respostas:
Sim.
Defina uma referência ao tempo de execução do MS Scripting ('Microsoft Scripting Runtime'). Conforme o comentário do @ regjo, vá em Ferramentas-> Referências e marque a caixa 'Microsoft Scripting Runtime'.
Crie uma instância do dicionário usando o código abaixo:
Set dict = CreateObject("Scripting.Dictionary")
ou
Dim dict As New Scripting.Dictionary
Exemplo de uso:
If Not dict.Exists(key) Then
dict.Add key, value
End If
Não se esqueça de definir o dicionário para Nothing
quando terminar de usá-lo.
Set dict = Nothing
keyed
.
Dim dict As New Scripting.Dictionary
sem a referência. Sem a referência, você deve usar o CreateObject
método de ligação tardia para instanciar esse objeto.
O VBA tem o objeto de coleção:
Dim c As Collection
Set c = New Collection
c.Add "Data1", "Key1"
c.Add "Data2", "Key2"
c.Add "Data3", "Key3"
'Insert data via key into cell A1
Range("A1").Value = c.Item("Key2")
O Collection
objeto executa pesquisas baseadas em chave usando um hash, para que seja rápido.
Você pode usar uma Contains()
função para verificar se uma coleção específica contém uma chave:
Public Function Contains(col As Collection, key As Variant) As Boolean
On Error Resume Next
col(key) ' Just try it. If it fails, Err.Number will be nonzero.
Contains = (Err.Number = 0)
Err.Clear
End Function
Editar 24 de junho de 2015 : Contains()
Agradecimentos mais curtos a @TWiStErRob.
Editar 25 de setembro de 2015 : adicionado um Err.Clear()
agradecimento a @scipilot.
ContainsKey
; alguém lendo apenas a invocação pode confundi-la por verificar se ela contém um valor específico.
O VBA não possui uma implementação interna de um dicionário, mas, no VBA, você ainda pode usar o objeto de dicionário da MS Scripting Runtime Library.
Dim d
Set d = CreateObject("Scripting.Dictionary")
d.Add "a", "aaa"
d.Add "b", "bbb"
d.Add "c", "ccc"
If d.Exists("c") Then
MsgBox d("c")
End If
Um exemplo de dicionário adicional que é útil para conter a frequência de ocorrência.
Fora do loop:
Dim dict As New Scripting.dictionary
Dim MyVar as String
Dentro de um loop:
'dictionary
If dict.Exists(MyVar) Then
dict.Item(MyVar) = dict.Item(MyVar) + 1 'increment
Else
dict.Item(MyVar) = 1 'set as 1st occurence
End If
Para verificar a frequência:
Dim i As Integer
For i = 0 To dict.Count - 1 ' lower index 0 (instead of 1)
Debug.Print dict.Items(i) & " " & dict.Keys(i)
Next i
Criando a resposta de cjrh , podemos criar uma função Contains que não requer etiquetas (não gosto de usar etiquetas).
Public Function Contains(Col As Collection, Key As String) As Boolean
Contains = True
On Error Resume Next
err.Clear
Col (Key)
If err.Number <> 0 Then
Contains = False
err.Clear
End If
On Error GoTo 0
End Function
Para um projeto meu, escrevi um conjunto de funções auxiliares para tornar o Collection
comportamento mais parecido com a Dictionary
. Ele ainda permite coleções recursivas. Você notará que Key sempre vem em primeiro lugar porque era obrigatório e fazia mais sentido na minha implementação. Eu também usei apenas String
teclas. Você pode mudar de volta, se quiser.
Renomeei isso para definir, porque ele substituirá os valores antigos.
Private Sub cSet(ByRef Col As Collection, Key As String, Item As Variant)
If (cHas(Col, Key)) Then Col.Remove Key
Col.Add Array(Key, Item), Key
End Sub
O err
material é para objetos, pois você passaria objetos usando set
e variáveis sem. Eu acho que você pode apenas verificar se é um objeto, mas fui pressionado pelo tempo.
Private Function cGet(ByRef Col As Collection, Key As String) As Variant
If Not cHas(Col, Key) Then Exit Function
On Error Resume Next
err.Clear
Set cGet = Col(Key)(1)
If err.Number = 13 Then
err.Clear
cGet = Col(Key)(1)
End If
On Error GoTo 0
If err.Number <> 0 Then Call err.raise(err.Number, err.Source, err.Description, err.HelpFile, err.HelpContext)
End Function
O motivo deste post ...
Public Function cHas(Col As Collection, Key As String) As Boolean
cHas = True
On Error Resume Next
err.Clear
Col (Key)
If err.Number <> 0 Then
cHas = False
err.Clear
End If
On Error GoTo 0
End Function
Não joga se não existir. Apenas garante que ele seja removido.
Private Sub cRemove(ByRef Col As Collection, Key As String)
If cHas(Col, Key) Then Col.Remove Key
End Sub
Obtenha uma matriz de chaves.
Private Function cKeys(ByRef Col As Collection) As String()
Dim Initialized As Boolean
Dim Keys() As String
For Each Item In Col
If Not Initialized Then
ReDim Preserve Keys(0)
Keys(UBound(Keys)) = Item(0)
Initialized = True
Else
ReDim Preserve Keys(UBound(Keys) + 1)
Keys(UBound(Keys)) = Item(0)
End If
Next Item
cKeys = Keys
End Function
Se, por qualquer motivo, você não puder instalar recursos adicionais no Excel ou não desejar, também poderá usar matrizes, pelo menos para problemas simples. Como WhatIsCapital, você coloca o nome do país e a função retorna seu capital.
Sub arrays()
Dim WhatIsCapital As String, Country As Array, Capital As Array, Answer As String
WhatIsCapital = "Sweden"
Country = Array("UK", "Sweden", "Germany", "France")
Capital = Array("London", "Stockholm", "Berlin", "Paris")
For i = 0 To 10
If WhatIsCapital = Country(i) Then Answer = Capital(i)
Next i
Debug.Print Answer
End Sub
Dim
palavra-chave Country
e Capital
precisa ser declarada como Variante devido ao uso de Array()
, i
deve ser declarada (e deve ser se Option Explicit
for definida), e o contador de loop lançará um erro fora do limite - mais seguro para use UBound(Country)
para o To
valor Talvez também seja interessante notar que, embora a Array()
função seja um atalho útil, não é a maneira padrão de declarar matrizes no VBA.
Todos os outros já mencionaram o uso da versão scripting.runtime da classe Dictionary. Se você não conseguir usar esta DLL, também poderá usar esta versão, basta adicioná-la ao seu código.
https://github.com/VBA-tools/VBA-Dictionary/blob/master/Dictionary.cls
É idêntico à versão da Microsoft.