Tabla hash / matriz asociativa en VBA

Respuestas:

109

Creo que está buscando el objeto Diccionario, que se encuentra en la biblioteca de Microsoft Scripting Runtime. (Agregue una referencia a su proyecto desde el menú Herramientas ... Referencias en el VBE).

Prácticamente funciona con cualquier valor simple que pueda caber en una variante (las claves no pueden ser matrices, y tratar de convertirlas en objetos no tiene mucho sentido. Vea el comentario de @Nile a continuación):

Dim d As dictionary
Set d = New dictionary

d("x") = 42
d(42) = "forty-two"
d(CVErr(xlErrValue)) = "Excel #VALUE!"
Set d(101) = New Collection

También puede usar el objeto Colección VBA si sus necesidades son más simples y solo desea claves de cadena.

No sé si ninguno de los dos realmente tiene hash en algo, por lo que es posible que desee profundizar más si necesita un rendimiento similar a una tabla hash. (EDITAR: Scripting.Dictionary usa una tabla hash internamente).

jtolle
fuente
sí, el diccionario es la respuesta. También encontré la respuesta en este sitio. stackoverflow.com/questions/915317/…
user158017
2
Esa es una respuesta bastante buena: pero las claves nunca son objetos; lo que en realidad está sucediendo es que la propiedad predeterminada del objeto se lanza como una cadena y se usa como clave. Esto no funciona si el objeto no tiene una propiedad predeterminada (generalmente 'nombre') definida.
Nigel Heffernan
@Nile, gracias. Veo que de hecho tienes razón. También parece que si el objeto no tiene una propiedad predeterminada, la clave de diccionario correspondiente es Empty. Edité la respuesta en consecuencia.
jtolle
Varias estructuras de datos explicadas aquí: analystcave.com/… Esta publicación muestra cómo usar tablas hash .NEXT en Excel VBA- stackoverflow.com/questions/8677949/…
johny why
error tipográfico del enlace anterior: .NET, no .NEXT.
Johnny Why
6

Aquí vamos ... simplemente copie el código en un módulo, está listo para usar

Private Type hashtable
    key As Variant
    value As Variant
End Type

Private GetErrMsg As String

Private Function CreateHashTable(htable() As hashtable) As Boolean
    GetErrMsg = ""
    On Error GoTo CreateErr
        ReDim htable(0)
        CreateHashTable = True
    Exit Function

CreateErr:
    CreateHashTable = False
    GetErrMsg = Err.Description
End Function

Private Function AddValue(htable() As hashtable, key As Variant, value As Variant) As Long
    GetErrMsg = ""
    On Error GoTo AddErr
        Dim idx As Long
        idx = UBound(htable) + 1

        Dim htVal As hashtable
        htVal.key = key
        htVal.value = value

        Dim i As Long
        For i = 1 To UBound(htable)
            If htable(i).key = key Then Err.Raise 9999, , "Key [" & CStr(key) & "] is not unique"
        Next i

        ReDim Preserve htable(idx)

        htable(idx) = htVal
        AddValue = idx
    Exit Function

AddErr:
    AddValue = 0
    GetErrMsg = Err.Description
End Function

Private Function RemoveValue(htable() As hashtable, key As Variant) As Boolean
    GetErrMsg = ""
    On Error GoTo RemoveErr

        Dim i As Long, idx As Long
        Dim htTemp() As hashtable
        idx = 0

        For i = 1 To UBound(htable)
            If htable(i).key <> key And IsEmpty(htable(i).key) = False Then
                ReDim Preserve htTemp(idx)
                AddValue htTemp, htable(i).key, htable(i).value
                idx = idx + 1
            End If
        Next i

        If UBound(htable) = UBound(htTemp) Then Err.Raise 9998, , "Key [" & CStr(key) & "] not found"

        htable = htTemp
        RemoveValue = True
    Exit Function

RemoveErr:
    RemoveValue = False
    GetErrMsg = Err.Description
End Function

Private Function GetValue(htable() As hashtable, key As Variant) As Variant
    GetErrMsg = ""
    On Error GoTo GetValueErr
        Dim found As Boolean
        found = False

        For i = 1 To UBound(htable)
            If htable(i).key = key And IsEmpty(htable(i).key) = False Then
                GetValue = htable(i).value
                Exit Function
            End If
        Next i
        Err.Raise 9997, , "Key [" & CStr(key) & "] not found"

    Exit Function

GetValueErr:
    GetValue = ""
    GetErrMsg = Err.Description
End Function

Private Function GetValueCount(htable() As hashtable) As Long
    GetErrMsg = ""
    On Error GoTo GetValueCountErr
        GetValueCount = UBound(htable)
    Exit Function

GetValueCountErr:
    GetValueCount = 0
    GetErrMsg = Err.Description
End Function

Para usar en su aplicación VB (A):

Public Sub Test()
    Dim hashtbl() As hashtable
    Debug.Print "Create Hashtable: " & CreateHashTable(hashtbl)
    Debug.Print ""
    Debug.Print "ID Test   Add V1: " & AddValue(hashtbl, "Hallo_0", "Testwert 0")
    Debug.Print "ID Test   Add V2: " & AddValue(hashtbl, "Hallo_0", "Testwert 0")
    Debug.Print "ID Test 1 Add V1: " & AddValue(hashtbl, "Hallo.1", "Testwert 1")
    Debug.Print "ID Test 2 Add V1: " & AddValue(hashtbl, "Hallo-2", "Testwert 2")
    Debug.Print "ID Test 3 Add V1: " & AddValue(hashtbl, "Hallo 3", "Testwert 3")
    Debug.Print ""
    Debug.Print "Test 1 Removed V1: " & RemoveValue(hashtbl, "Hallo_1")
    Debug.Print "Test 1 Removed V2: " & RemoveValue(hashtbl, "Hallo_1")
    Debug.Print "Test 2 Removed V1: " & RemoveValue(hashtbl, "Hallo-2")
    Debug.Print ""
    Debug.Print "Value Test 3: " & CStr(GetValue(hashtbl, "Hallo 3"))
    Debug.Print "Value Test 1: " & CStr(GetValue(hashtbl, "Hallo_1"))
    Debug.Print ""
    Debug.Print "Hashtable Content:"

    For i = 1 To UBound(hashtbl)
        Debug.Print CStr(i) & ": " & CStr(hashtbl(i).key) & " - " & CStr(hashtbl(i).value)
    Next i

    Debug.Print ""
    Debug.Print "Count: " & CStr(GetValueCount(hashtbl))
End Sub
Stefan0410
fuente
18
No voy a rechazar a un usuario nuevo que publica código, pero normalmente llamar a algo una "tabla hash" implica que la implementación subyacente es en realidad una tabla hash. Lo que tiene aquí es una matriz asociativa implementada con una matriz regular más una búsqueda lineal. Vea aquí la diferencia: en.wikipedia.org/wiki/Hash_table
jtolle
7
En efecto. El objetivo de una tabla hash es el 'hash' de la clave que lleva a la ubicación de su valor en el almacenamiento subyacente (o al menos lo suficientemente cerca, en el caso de claves duplicadas permitidas), eliminando así la necesidad de una búsqueda potencialmente costosa.
Cor_Blimey
3
Demasiado lento para tablas hash más grandes. Agregar 17,000 entradas toma más de 15 segundos. Puedo agregar 500,000 en menos de 6 segundos usando el diccionario. 500,000 en menos de 3 segundos usando mscorlib hashtable.
Christopher Thomas Nicodemus