Scripting.Dictionary Lookup-if-if-not-present只有一个密钥搜索?

时间:2011-08-02 11:01:53

标签: scripting dictionary vbscript scripting.dictionary

我正在查找Scripting.Dictionary中的密钥,以确保我只将它们(及其项目)添加到字典中一次:

If MyDict.Exists (Key) Then ' first internal key-value lookup
  Set Entry=MyDict.Item (Key)  ' Second internal key->value lookup
else
  Set Entry=New ItemType
  MyDict.Add Key,Entry
End If
' Now I work on Entry...

Exists查找字典中的密钥,Item ()也可以查找。所以我得到一个逻辑查找操作的两个键查找。是不是有更好的方法?

Item属性的dox说

  

“如果在尝试返回现有项目时未找到密钥,则为新项目   创建密钥并将其相应的项留空。“    (MSDN

这是真的,即查找一个不存在的键显然使这个字典的关键部分,可能与关联项=空。 但那有什么好处呢? 我如何使用它将其归结为一次查找操作?如何在Item ()属性调用期间创建密钥后设置空项?

2 个答案:

答案 0 :(得分:2)

此代码和输出:

>> Set d = CreateObject("Scripting.Dictionary")
>> WScript.Echo 0, d.Count
>> If d.Exists("soon to come") Then : WScript.Echo 1, d.Count : End If
>> WScript.Echo 2, d.Count
>> d("soon to come") = d("soon to come") + 1
>> WScript.Echo 3, d.Count, d("soon to come")
>>
0 0
2 0
3 1 1

所示:

  1. 使用.Exists查找不存在的密钥将密钥添加到字典中(.Count在#2处仍为0)
  2. 通过.Item或()访问非现有密钥 - 如我的示例代码中赋值的右侧 - 向字典添加一个键/空对;对于某些任务(例如频率计数),这个“有效”,因为空被视为0或字符串连接中的“”。这个小规模autovivification不能用于对象(没有合适的方法将Empty映射到程序员想到的任何对象,并且没有默认魔法,如Python或Ruby中的VBScript)
  3. 如果你必须维护一个命名对象的字典并且可以同时访问该名称及其对象,你可以写Set d(name) = object - 如果需要,d(name)将创建密钥槽“name”并且Set赋值将把对象放入相应的值(覆盖Empty或'old'对象('pointer'))。
  4. 如果你追加一些你真正希望实现的细节,我愿意加上这个答案。

    添加了:

    如果您(逻辑上)处理具有重复项的键列表,则必须添加新项 在飞行中对象到字典,你无法避免双重查找,因为 你需要检查存在(1.0)和分配(2.0)(也许是新的 创建并赋值(1.5))对象到你的工作变量(参见/ m:a或/ m:b in my 示例代码)。具有提供值的语句的其他语言可能允许

    之类的东西
    if ! (oBJ = dicX( key )) {
       oBJ = dicX( key ) = new ItemType() 
    }
    oBJ.doSomething()
    

    没有VBScript的Set vs. Let令人厌恶的东西,比如

    oBJ = dicX( key )
    If IsEmpty( oBJ ) Then
       dicX( key ) = New ItemType
       oBJ = dicX( key ) 
    End If
    

    只会为新元素做额外的工作,但这只是一个白日梦。

    如果那些双重查找真的很重要(我怀疑 - 你能给出一个论点吗? 或证据?),那么你的程序的整体设计确实很重要。例如: 如果你能把你的工作清单独一无二,那么一切都变得简单了(参见/ m:c in my 样品)。不可否认,我仍然不知道这些变化是否可能 为了你的具体任务。

    试验代码:

    Dim dicX  : Set dicX = CreateObject( "Scripting.Dictionary" )
    Dim aKeys : aKeys    = Split( "1 2 3 4 4 3 2 1 5" )
    Dim sMode : sMode    = "a"
    Dim oWAN  : Set OWAN = WScript.Arguments.Named
    If oWAN.Exists( "m" ) Then sMode = oWAN( "m" )
    Dim sKey, oBJ
    Select Case sMode
      Case "a"
        For Each sKey In aKeys
          If Not dicX.Exists( sKey ) Then 
             Set dicX( sKey ) = New cItemType.init( sKey )
          End If
          Set oBJ = dicX( sKey )
          WScript.Echo oBJ.m_sInfo
        Next    
      Case "b"  
        For Each sKey In aKeys
          If IsEmpty( dicX( sKey ) ) Then 
             Set dicX( sKey ) = New cItemType.init( sKey )
          End If   
          Set oBJ = dicX( sKey )
          WScript.Echo oBJ.m_sInfo
        Next    
      Case "c"  
        aKeys = uniqueList( aKeys )
        For Each sKey In aKeys
          Set dicX( sKey ) = New cItemType.init( sKey )
          Set oBJ = dicX( sKey )
          WScript.Echo oBJ.m_sInfo
        Next    
      Case Else
        WScript.Echo "Unknown /m:" & sMode & ", pick one of a, b, c."
    End Select
    WScript.Echo "----------"
    For Each sKey In dicX.Keys
        WScript.Echo dicX( sKey ).m_sInfo
    Next
    
    Dim g_ITCnt : g_ITCnt = 0
    Class cItemType
      Public m_sInfo
      Public Function init( sKey )
        Set init = Me
        g_ITCnt  = g_ITCnt + 1
        m_sInfo  = "Obj for " & sKey & " (" & g_ITCnt & ")"
      End Function   
    End Class ' cItemType
    
    Function uniqueList( aX )
      Dim dicU : Set dicU = CreateObject( "Scripting.Dictionary" )
      Dim vX
      For Each vX in aX
          dicU( vX ) = Empty
      Next    
      uniqueList = dicU.Keys
    End Function
    

    示例输出:

    /m:a
    Obj for 1 (1)
    Obj for 2 (2)
    Obj for 3 (3)
    Obj for 4 (4)
    Obj for 4 (4)
    Obj for 3 (3)
    Obj for 2 (2)
    Obj for 1 (1)
    Obj for 5 (5)
    ----------
    Obj for 1 (1)
    Obj for 2 (2)
    Obj for 3 (3)
    Obj for 4 (4)
    Obj for 5 (5)
    ==================================================
    xpl.vbs: Erfolgreich beendet. (0) [0.07031 secs]
    
    /m:c
    Obj for 1 (1)
    Obj for 2 (2)
    Obj for 3 (3)
    Obj for 4 (4)
    Obj for 5 (5)
    ----------
    Obj for 1 (1)
    Obj for 2 (2)
    Obj for 3 (3)
    Obj for 4 (4)
    Obj for 5 (5)
    ================================================
    xpl.vbs: Erfolgreich beendet. (0) [0.03906 secs]
    

    时序差异可能是由于/ m:c的输出减少造成的 模式,但它强调了不经常做某事的重要性 然后必要的。

答案 1 :(得分:0)

对我来说,关键问题是VBScript迫使我们对对象使用Set,而Empty不是对象。我过去用来解决这个问题的一个技巧是使用Array函数为值创建一个临时占位符。然后我可以检查数组以查看该值是否为对象。适用于您的示例:

tempArr = Array(dict.Item(key))
If IsEmpty(tempArr(0)) Then
    ' new entry
    Set entry = New MyClass
    ' can't use Add because the key has already been implicitly created
    Set dict.Item(key) = entry
Else
    ' existing entry
    Set entry = tempArr(0)
End If

在这种情况下,您还没有消除双重查找,而是将其从“现有条目”案例移至“新条目”案例。