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

时间:2018-01-22 14:48:41

标签: 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

我想创建一个类似字典的对象,就像这样(psuedocode):

Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
dict.Add [Chevy, Lumina, 2003], $75
dict.Add [Chevy, Camaro, 2018], $50
dict.Add [Dodge, Charger, 2004], $13
dict.Add [Toyota, Camry, 2015], $35

基本上,我想要[KeyCol1,KeyCol2,KeyCol3],ValueCol1

的键值对

但字典不能有键的数组,所以我有点卡住了。有什么东西可以让我获得字典的O(1)性能,但是数组是“键”吗?

谢谢大家。

2 个答案:

答案 0 :(得分:3)

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

为了好玩,您还可以创建一个词典树。为此你可以使用这些功能:

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

这里的优点是各个密钥将其数据类型保存在数据结构中:例如数字键仍为数字。

更多Generic

如果还需要将值与部分复合键相关联,则上述内容是不够的。在这种情况下,创建一个真实树,其中每个节点可以同时具有值​​节点和子节点。这可以通过更改上面的Sub和Function来完成,如下所示:

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

答案 1 :(得分:2)

使用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(key)

ParamArray的想法是你可以提供任意数量的参数。