Vba 是否有类似字典的对象允许我将数组存储为键?

Vba 是否有类似字典的对象允许我将数组存储为键?,vba,excel,Vba,Excel,假设我在Excel中有某种ListObject,如下所示: KeyCol1 KeyCol2 KeyCol3 ValueCol1 Chevy Lumina 2003 $75 Chevy Camaro 2018 $50 Dodge Charger 2004 $13 Toyota Camry 2015 $35 我想创建一个类似字典的对象

假设我在Excel中有某种ListObject,如下所示:

KeyCol1     KeyCol2     KeyCol3     ValueCol1
Chevy       Lumina      2003        $75
Chevy       Camaro      2018        $50
Dodge       Charger     2004        $13
Toyota      Camry       2015        $35
我想创建一个类似字典的对象,如so(psuedocode):

基本上,我想要[KeyCol1,KeyCol2,KeyCol3],ValueCol1的键,值对

但是字典不能为键设置数组,所以我有点卡住了。是否有任何东西可以让我获得字典的O(1)性能,但将数组作为“键”


谢谢大家。

使用
ParamArray
参数将3个值连接到一个字符串。如iby@trincot所述,唯一分隔符的概念是一个好主意:

Option Explicit

Sub TestMe()

    Dim dict As Object
    Set dict = CreateObject("Scripting.Dictionary")
    dict.Add addToString("Chevy", "Lumina", "2003"), 75
    dict.Add addToString("Chevy", "Camaro", "2018"), 50
    dict.Add addToString("Dodge", "Charger", "2004"), 13

    If dict.exists("uniqueChevyuniqueLuminaunique2003") Then
        Debug.Print dict("uniqueChevyuniqueLuminaunique2003")
    End If

End Sub

Public Function addToString(ParamArray myVar() As Variant) As String

    Dim cnt     As Long
    Dim val     As Variant
    Dim delim   As String: delim = "unique"

    For cnt = LBound(myVar) To UBound(myVar)
        addToString = addToString & delim & myVar(cnt)
    Next cnt

End Function
在添加到字典之前,最好检查给定的密钥是否存在<代码>dict.Exists(键)


ParamArray
的思想是,您可以根据需要提供任意多的参数。

使用
ParamArray
参数将3个值连接到一个字符串。如iby@trincot所述,唯一分隔符的概念是一个好主意:

Option Explicit

Sub TestMe()

    Dim dict As Object
    Set dict = CreateObject("Scripting.Dictionary")
    dict.Add addToString("Chevy", "Lumina", "2003"), 75
    dict.Add addToString("Chevy", "Camaro", "2018"), 50
    dict.Add addToString("Dodge", "Charger", "2004"), 13

    If dict.exists("uniqueChevyuniqueLuminaunique2003") Then
        Debug.Print dict("uniqueChevyuniqueLuminaunique2003")
    End If

End Sub

Public Function addToString(ParamArray myVar() As Variant) As String

    Dim cnt     As Long
    Dim val     As Variant
    Dim delim   As String: delim = "unique"

    For cnt = LBound(myVar) To UBound(myVar)
        addToString = addToString & delim & myVar(cnt)
    Next cnt

End Function
在添加到字典之前,最好检查给定的密钥是否存在<代码>dict.Exists(键)


ParamArray
的思想是,您可以根据需要提供任意多的参数。

您可以将数组元素连接到一个字符串,并将其用作键。根据实际键的不同,您可能需要使用分隔符,以便明确最终字符串的哪个部分与哪个键相关

为了好玩,您还可以创建字典树。为此,您可以使用以下功能:

Sub AddNested(dict As Object, keys As Variant, value As Variant)
    Dim parent As Object
    Dim i As Long
    Dim key As String

    Set parent = dict
    For i = LBound(keys) To UBound(keys) - 1
        key = keys(i)
        If Not parent.Exists(key) Then
            parent.Add key, CreateObject("Scripting.Dictionary")
        End If
        Set parent = parent(key)
    Next
    parent.Add keys(UBound(keys)), value
End Sub

Function GetNested(dict As Object, keys As Variant)
    Dim parent As Object
    Dim i As Long
    Dim key As String

    Set parent = dict
    For i = LBound(keys) To UBound(keys) - 1
        key = keys(i)
        If Not parent.Exists(key) Then
            Exit Function
        End If
        Set parent = parent(key)
    Next
    GetNested = parent(keys(UBound(keys)))
End Function
显示如何添加到此结构并从中读取的示例:

Dim dict As Object
Dim i As Long

Set dict = CreateObject("Scripting.Dictionary")
AddNested dict, Array("Chevy", "Lumina", 2003), 75
i = GetNested(dict, Array("Chevy", "Lumina", 2003))
Debug.Print i ' = 75
这里的优点是单个键在数据结构中保持其数据类型:例如,数字键保持数字

更一般 如果还需要将值与部分组合键相关联,则上述操作将不够。在这种情况下,创建一个真正的树,其中每个节点都可以有一个值和子节点。可通过如下更改上述子系统和功能来实现:

Sub AddNested(dict As Object, keys As Variant, value As Variant)
    Dim parent As Object
    Dim key As String
    Dim children As Object

    Set parent = tree
    For Each key In keys
        If Not parent.Exists("Children") Then
            parent.Add "Children", CreateObject("Scripting.Dictionary")
        End If
        Set children = parent("Children")
        If Not children.Exists(key) Then
            children.Add key, CreateObject("Scripting.Dictionary")
        End If
        Set parent = children(key)
    Next
    If parent.Exists("Value") Then parent.Remove "Value"
    parent.Add "Value", value
End Sub

Function GetNested(dict As Object, keys As Variant)
    Dim parent As Object
    Dim key As String
    Dim children As Object

    Set parent = tree
    For Each key In keys
        If Not parent.Exists("Children") Then Exit Function
        Set children = parent("Children")
        If Not children.Exists(key) Then Exit Function
        Set parent = children(key)
    Next
    GetNested = parent("Value")
End Function

您可以将数组元素连接到一个字符串,并将其用作键。根据实际键的不同,您可能需要使用分隔符,以便明确最终字符串的哪个部分与哪个键相关

为了好玩,您还可以创建字典树。为此,您可以使用以下功能:

Sub AddNested(dict As Object, keys As Variant, value As Variant)
    Dim parent As Object
    Dim i As Long
    Dim key As String

    Set parent = dict
    For i = LBound(keys) To UBound(keys) - 1
        key = keys(i)
        If Not parent.Exists(key) Then
            parent.Add key, CreateObject("Scripting.Dictionary")
        End If
        Set parent = parent(key)
    Next
    parent.Add keys(UBound(keys)), value
End Sub

Function GetNested(dict As Object, keys As Variant)
    Dim parent As Object
    Dim i As Long
    Dim key As String

    Set parent = dict
    For i = LBound(keys) To UBound(keys) - 1
        key = keys(i)
        If Not parent.Exists(key) Then
            Exit Function
        End If
        Set parent = parent(key)
    Next
    GetNested = parent(keys(UBound(keys)))
End Function
显示如何添加到此结构并从中读取的示例:

Dim dict As Object
Dim i As Long

Set dict = CreateObject("Scripting.Dictionary")
AddNested dict, Array("Chevy", "Lumina", 2003), 75
i = GetNested(dict, Array("Chevy", "Lumina", 2003))
Debug.Print i ' = 75
这里的优点是单个键在数据结构中保持其数据类型:例如,数字键保持数字

更一般 如果还需要将值与部分组合键相关联,则上述操作将不够。在这种情况下,创建一个真正的树,其中每个节点都可以有一个值和子节点。可通过如下更改上述子系统和功能来实现:

Sub AddNested(dict As Object, keys As Variant, value As Variant)
    Dim parent As Object
    Dim key As String
    Dim children As Object

    Set parent = tree
    For Each key In keys
        If Not parent.Exists("Children") Then
            parent.Add "Children", CreateObject("Scripting.Dictionary")
        End If
        Set children = parent("Children")
        If Not children.Exists(key) Then
            children.Add key, CreateObject("Scripting.Dictionary")
        End If
        Set parent = children(key)
    Next
    If parent.Exists("Value") Then parent.Remove "Value"
    parent.Add "Value", value
End Sub

Function GetNested(dict As Object, keys As Variant)
    Dim parent As Object
    Dim key As String
    Dim children As Object

    Set parent = tree
    For Each key In keys
        If Not parent.Exists("Children") Then Exit Function
        Set children = parent("Children")
        If Not children.Exists(key) Then Exit Function
        Set parent = children(key)
    Next
    GetNested = parent("Value")
End Function

只需将它们连接成一个字符串,可能带有一个不太可能出现在数据本身中的分隔符!我们可以做任何我们想做的事。不过,我们不应该这样做,因为那太恶心了。@trincot谢谢你,trincot。我也考虑过这一点,这似乎是我选择的最佳解决方案…@Blacksilver是的,我想我要做的是在多个列上加入SQL,我可能应该使用某种SQL解决方案。然而,我已经研究了如何使用SQL在Excel VBA中连接两个ListObjects,这看起来像是一场噩梦。您可以使用键的集合对象作为字典键,而不是数组。为每个键值对创建新的集合对象。只需将它们连接成一个字符串,可能带有一个不太可能出现在数据本身中的分隔符。换句话说:是!我们可以做任何我们想做的事。不过,我们不应该这样做,因为那太恶心了。@trincot谢谢你,trincot。我也考虑过这一点,这似乎是我选择的最佳解决方案…@Blacksilver是的,我想我要做的是在多个列上加入SQL,我可能应该使用某种SQL解决方案。然而,我已经研究了如何使用SQL在Excel VBA中连接两个ListObjects,这看起来像是一场噩梦。您可以使用键的集合对象作为字典键,而不是数组。对于每个键值对,您都创建了新的集合对象。您的答案非常好,如果我能接受两个答案,我会接受您和trincot的答案。我很抱歉:(我将按照你们两人的建议对密钥使用连接。你的答案非常好,如果我能接受两个答案,我会接受你的和trincot的。我很抱歉:(我将按照你们两人的建议使用键的连接。通常,解决方案很有趣,但在以下情况下会出现中断:
AddNested dict,Array(“Lumina”,“Lumina”,“Lumina”),3:AddNested dict,Array(“Lumina”,“Lumina”),2:AddNested dict,Array(“Lumina”),1
或者如果数组由1个元素组成。@Vityata,正确,我假设复合键中的键数是常量。我添加了一个更通用的版本,它将正确处理这些数组中的不同计数。一般来说,解决方案很有趣,但在以下情况下会出现问题:
AddNested dict,数组(“Lumina”,“Lumina”,“Lumina”),3:add嵌套dict,数组(“Lumina”,“Lumina”),2:add嵌套dict,数组(“Lumina”),1
或者如果数组由1个元素组成。@Vityata,正确,我假设复合键中的键数是常量。我添加了一个更通用的版本,可以正确处理这些数组中不同的计数。