Usar diccionarios en VBA de Microsoft Access 2010

Usar diccionarios en VBA de Microsoft Access 2010

Ruperto Coronado
Ruperto Coronado

Me ha encantado poder utilizar diccionarios en MS Access. Así puedo -por ejemplo- hacer que una función retorne más de un valor, pasar más de un parámetro a una función o simplemente guardar una colección de valores en formato clave:valor. Lo mejor de python en VBA.

Un diccionario es básicamente un objeto que contiene una lista de claves y valores. Por ejemplo:

"nombre": "Ruperto", 
"apellidos": "Coronado"

Básicamente como un objeto de Javascript o tal cual, un diccionario en Python.

¿Cómo usar diccionarios en VBA?

Primero debes ir al menú de herramientas y seleccionar la opción de referencias. Luego en la ventana de referencias seleccionar la opción de Microsoft Scripting Runtime. Esto te permitirá utilizar diccionarios en VBA.

Referencias en VBA

A partir de aquí ya puedes empezar a declar y usar diccionarios como en el siguiente ejemplo:

Public Sub ProbarDiccionarios()
    Dim d as New Dictionay
    d.Add "nombre", "Ruperto"
    d.Add "apellidos", "Coronado"
    
    Debug.Print d("nombre") ' Imprime "Ruperto"
    Debug.Print d("apellidos") ' Imprime "Coronado"

    'O puedes agregar directamente la clave:
    d("edad") = 30

    'Incluso puedes usar un subdiccionario
    set d("habilidades") As New Dictionary
    d("habilidades")("1") = "Programación"
    'O mediante métodos
    d("habilidades").add "2", "comer"
End Sub

¿Cómo recorrer (o iterar sobre) un diccionario?

Puede darse el caso en donde no conozcas las claves del diccionario o sencillamente tienes que iterarlo para hacer algunas operaciones

Public Sub IterarDiccionario()
    Dim d as New Dictionay
    Dim item as Variant

    d.Add "nombre", "Ruperto"
    d.Add "apellidos", "Coronado"

    for each item in d
        debug.print item, d(item) 'Imprime: clave:valor
    next
    
end sub

¿Cómo hacer que una función retorne un diccionario en VBA?

Public Function ObtenerDiccionario() As Dictionary '<-- Esta es la clave
    Dim d as New Dictionay
    d.Add "nombre", "Ruperto"
    d.Add "apellidos", "Coronado"
    
    Set ObtenerDiccionario = d '<-- No olvides el Set que de otra manera no funcionará
End Function

Public Sub OperarDiccionario()
    Dim d as Dictionary

    Set d = ObtenerDiccionario()

    dim item as Variant

    for each item in d
        debug.print item, d(item)
    next
End Sub

Consideraciones importantes

Métodos miembros de Dictionary Estos son los métodos que proporciona la clase Dictionary

Es muy importante saber que por defecto el método de comparación de claves está establecido en BinaryCompare lo que hace que la comparación sea byte por byte por lo que será sensible a mayúsculas y minúsculas, de modo que d("hola") y d("Hola") podrian contener diferentes valores. Puedes establecerla en TextCompare usando el método .CompareMode

También ten cuidado con el tipo de datos que tendrá la clave, porque sabes que 1<>“1”. Esto es especialmente importante cuando usas una variable del tipo Variant para establecer las claves del diccionario. Yo sugiero que siempre conviertas la clave a String antes de usarla.

Por ejemplo:

Private sub ProbarDiccionario()
    Dim d as New Dictionary
    d.Add "nombre", "Ruperto"
    d.Add "apellidos", "Coronado"
    d.add "1", "2"
    
    debug.print d(1) 'Esto no funcionará porque la clave fue añadida como String
    debug.print d("1") 'Esto sí
    
    Dim intNumero as integer 'O cualquier otro tipo
    intNumero = 1

    d(intNumero) 'Esto no funcionara
    d(CStr(intNumero)) 'Esto sí funcionará

    'Por costumbre general cuando no sé el tipo de la variable que usaré para hacer referencia a una clave siempre uso CStr para convertirlo a String
End Sub

En este ejemplo asigno el contenido de un Recordset a un diccionario:

Sub ProbarDict()
    Dim RS As New ADODB.Recordset
    Dim d As Dictionary
    Dim Campo As Variant
    
    With RS
        .Open "SELECT * FROM Producto", CurrentProject.Connection, adOpenStatic, adLockReadOnly
        While Not .EOF
            Set d(CStr(!ID)) = New Dictionary 'uso el ID como indice de elementos del diccionario
            For Each Campo In .Fields
                d(CStr(!ID))(CStr(Campo.Name)) = Campo.value
            Next
            .MoveNext
        Wend
        .Close
    End With
    
    Set RS = Nothing
    
    Dim item As Variant
    Dim subItem As Variant
    Dim sD As Dictionary 'Sub diccionario que contiene los campos y valores de cada producto
    'Imprimir los valores del diccionario
    For Each item In d
        Debug.Print "Producto ID: " & item
        For Each subItem In d(item)
            Debug.Print vbTab & subItem & ":" & d(item)(subItem)
        Next
    Next
    'Se imprimirá:
    'Producto ID: _
        Nombre:JABON DE BARRA 200 GR _
        Marca: ZOTE _
        Precio: 15.00
End Sub

Por favor considera que es un ejemplo rápido y podría optimizarse más. Por ejemplo, podria detectarse el tipo de campo y asignar comillas a los valores cuando sean DATETIME o CHAR