如何更改集合项的值

How to change value of an item of a collection

提问人:genespos 提问时间:4/9/2015 最后编辑:Communitygenespos 更新时间:2/28/2023 访问量:32873

问:

使用此代码(在 excel-vba 中),我根据数组向集合中添加了许多项目。
我使用数组的值作为键,使用字符串“NULL”作为添加的每个项目的值。

Dim Coll As New collection
Dim myArr()

Set Coll = New collection
myArr() = Array("String1", "String2", "String3")

For i = LBound(myArr) To UBound(myArr)
    Coll.Add "NULL", myArr(i)
Next i

现在,如果我想更改项目的值,通过键标识它,我必须删除该项目,然后添加具有相同键的项目,或者是否可以更改项目值?

这是唯一的方法吗?

Coll.Remove "String1"
Coll.Add "myString", "String1"

或者有类似的东西:(我知道这行不通)

Coll("String1") = "myString"
VBA Excel 集合

评论

5赞 Mathieu Guindon 4/9/2015
您是否尝试过使用(来自脚本库)来代替?Dictionary
0赞 L42 4/9/2015
AFAIK,正如MSDN所说,这是唯一的方法。马克杯是正确的,请改用字典。如果您决定走这条路,请查看此路线。
1赞 Amen Jlili 4/9/2015
你不能用收集来做到这一点。
0赞 genespos 4/9/2015
好的,只有另一个问题:使用字典而不是集合有什么禁忌症吗?
0赞 Amen Jlili 4/9/2015
是的。这将对你有所帮助。前几天我找到了它:youtube.com/watch?v=dND4coLI_B8

答:

15赞 Jurrian Fahner 5/23/2015 #1

还可以编写一个(公共)函数来对集合进行更新。

public function updateCollectionWithStringValue(coll as Collection, key as string, value as string) as collection
    coll.remove key
    coll.add value, key
    set updateCollectionWithStringValue = coll
end function

您可以通过以下方式调用此函数:

set coll = updateCollectionWithStringValue(coll, "String1","myString")

然后,您有一个可以调用的衬里。

评论

0赞 Anton Rybalko 11/23/2020
错别字:必须是coll ax Collectioncoll as Collection
2赞 GeertVc 6/21/2018 #2

你不能使用这个参数来满足这个要求吗?Before

例:

Option Explicit

Sub TestProject()
    Dim myStrings As New Collection

    myStrings.Add item:="Text 1"
    myStrings.Add item:="Text 2"
    myStrings.Add item:="Text 3"

    ' Print out the content of collection "myStrings"
    Debug.Print "--- Initial collection content ---"
    PrintCollectionContent myStrings
    ' Or with the "Call" keyword: Call PrintCollectionContent(myStrings)
    Debug.Print "--- End Initial collection content ---"

    ' Now we want to change "Text 2" into "New Text"
    myStrings.Add item:="New Text", Before:=2 ' myStrings will now contain 4 items
    Debug.Print "--- Collection content after adding the new content ---"
    ' Print out the 'in-between' status of collection "myStrings" where we have
    ' both the new string and the string to be replaced still in.
    PrintCollectionContent myStrings
    Debug.Print "--- End Collection content after adding the new content ---"

    myStrings.Remove 3
    ' Print out the final status of collection "myStrings" where the obsolete 
    ' item is removed
    Debug.Print "--- Collection content after removal of the old content ---"
    PrintCollectionContent myStrings
    Debug.Print "--- End Collection content after removal of the old content ---"

End Sub

Private Sub PrintCollectionContent(ByVal myColl As Variant)
    Dim i as Integer

    For i = 1 To myColl.Count()
        Debug.Print myColl.Item(i)
    Next i
End Sub

这不应该完成这项工作吗?

评论

0赞 Marcelo Scofano Diniz 4/7/2020
+1,因为它满足了OP的要求;但为此,你必须修复:1)前两行实际上只有一行,你留下了一个疯狂的剩余(Set myStringsRef = myStrings),既没有声明也没有必要;2) PrintCollectionContent (myStringsRef) - 或 (myStrings) 您的调用 - 需要在之前使用 Design,或者在之后删除括号。
0赞 GeertVc 4/8/2020
@MarceloScofano:你说得对,我用了“疯狂的剩菜”,因为当时我不知道什么时候用或不用(我只知道一个参数,现在你不必用,一开始很混乱)。因此,我使用了那个“无用”变量。我也没有在我的示例代码中使用 ,因此没有必要先声明它。作为当时的 VBA 新手,我现在知道我不想再工作了,而且我现在也更了解工作方式......我编辑了我的答案。CallCallmyStringsRefOption ExplicitOption ExplicitCollections
0赞 Marcelo Scofano Diniz 4/9/2020
是的,我说疯了,因为起初我发誓它适合代码,所以我声明了 myStringsRef 并弄得一团糟!只花了几分钟后,我决定摆脱它,并可以看到你的答案合适。感谢您的回答,它使我确信我不必使用字典来满足我的需求。供路过这里并且不明白为什么 Call () 与 no() 的每个人参考,这里有一个解释。
0赞 Keith 3/16/2021 #3

只需循环集合并将新值添加到新集合中即可...

function prep_new_collection(my_old_data as collection) as collection

dim col_data_prep as new collection

for i = 1 to my_old_data.count

if my_old_data(i)(0)= "whatever" then

  col_data_prep.add array("NULL", my_old_data(i)(1))

else

 col_data_prep.add array(my_old_data(i)(0), my_old_data(i)(1))

end if

next i

 set prep_new_collection = col_data_prep

end function
0赞 Mammad 3/24/2021 #4

我刚刚遇到了同样的问题,想在这里为任何可能需要它的人发布我的解决方案。我的解决方案是制作一个名为 EnhancedCollection 的类,该类具有更新函数。将此代码保存到名为 EnhancedCollection.cls 的文件中,然后导入到项目中。

    VERSION 1.0 CLASS
    BEGIN
      MultiUse = -1  'True
    END
    Attribute VB_Name = "EnhancedCollection"
    Attribute VB_GlobalNameSpace = False
    Attribute VB_Creatable = False
    Attribute VB_PredeclaredId = False
    Attribute VB_Exposed = False
    Option Explicit
    
    Private data As New Collection
       
    '=================================ADD
    
    If IsMissing(key) Then
        If IsMissing(before) Then
            If IsMissing(after) Then
                data.Add Value
            Else
                data.Add Value, , , after
            End If
        Else
            data.Add Value, , before
        End If
    ElseIf key = "TEMP_ITEM" Then
        Exit Sub
    Else
        If IsMissing(before) Then
            If IsMissing(after) Then
                data.Add Value, key
            Else
                data.Add Value, key, , after
            End If
        Else
            data.Add Value, key, before
        End If
    End If
End Sub
'=================================REMOVE

Sub Remove(key As Variant)
    data.Remove key
End Sub

    '=================================COUNT
    
    Function Count() As Integer
        Count = data.Count
    End Function
    '=================================ITEM
    
    Function Item(key As Variant) As Variant
    'This is the default Function of the class
    Attribute Item.VB_Description = "returns the item"
    Attribute Item.VB_UserMemId = 0
    On Error GoTo OnError
        If VarType(key) = vbString Or VarType(key) = vbInteger Then
            Item = data.Item(key)
        End If
        Exit Function
    OnError:
        Item = Null
    End Function
    '=================================Update
    
    Function Update(key As Variant, Value As Variant) As Variant
    On Error GoTo OnError
        If VarType(key) = vbString Or VarType(key) = vbInteger Then
            data.Add "", "TEMP_ITEM", , key
            data.Remove key
            data.Add Value, key, "TEMP_ITEM"
            data.Remove "TEMP_ITEM"
        End If
        Exit Function
    OnError:
        Update = Null
    End Function

作为额外的好处,您可以随时添加更多功能。

2赞 Tom Robinson 8/15/2021 #5

这是一个有效的解决方案Coll("String1") = "myString"

将对象添加到 VBA 集合时,将添加对象本身,而不是其值。这意味着,当对象位于集合中时,您可以更改该对象的属性。我创建了一个类模块,该模块将单个变体包装在类对象中,并将其作为其默认属性。将其保存到文件中,然后在 VBA 编辑器中保存。.Add.Value.clsFile > Import File

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "clsValue"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Compare Database
Option Explicit
Private MyValue As Variant
Property Get Value() As Variant
    Attribute Value.VB_UserMemId = 0
    Value = MyValue
End Property
Property Let Value(v As Variant)
    Attribute Value.VB_UserMemId = 0
    MyValue = v
End Property

现在,此版本的代码按照您希望的方式工作:

Private Sub clsValue_test()
Dim Coll As New Collection
Dim myArr()
Dim v As Variant
myArr = Array("String1", "String2", "String3")
For Each v In myArr
    Coll.Add New clsValue, v
    Coll(v) = "NULL"
Next v
Coll("String1") = "myString"    ' it works!
For Each v In myArr
    Debug.Print v, ": "; Coll(v)
Next v
End Sub

生成结果:

String1       : myString
String2       : NULL
String3       : NULL
2赞 flodis 9/29/2021 #6

制作按键删除集合项的函数的一种变体是将其实现为 VBA 属性

Public Property Let CollectionValue(coll As Collection, key As String, value As String)
    On Error Resume Next
    coll.Remove key
    On Error GoTo 0
    coll.Add value, key
End Property


Public Property Get CollectionValue(coll As Collection, key As String) As String
    CollectionValue = coll(key)
End Property

并像这样使用

'Writing
CollectionValue(coll, "Date") = Now()

'Reading
Debug.Print(CollectionValue(coll, "Date")) 

通过忽略 key 是否不存在,它也可以用于添加项目

0赞 abakum 2/28/2023 #7
Sub tcoll()
 Dim c As New Collection
 c.Add Array("1", 2, False)
 c.Add Array("2", 3, False)
 c.Add Array("1", 4, False)
 For Each ci In c:  Debug.Print ci(0), ci(1), ci(2): Next
 If 1 Then
  'ok
  For X = c.Count To 1 Step -1
   Select Case c(X)(0)
   Case "1"
    c.Add Array(c(X)(0), c(X)(1), 1), after:=X
    c.Remove X
   Case "2"
    c.Remove X
   End Select
  Next
 Else
  'Subscript out of range
  For X = 1 To c.Count
   Select Case c(X)(0)
   Case "1"
    c(X)(2) = 1 'no error but collection is not changed
   Case "2"
    c.Remove X
   End Select
  Next
 End If
 For Each ci In c:  Debug.Print ci(0), ci(1), ci(2): Next
 Set c = Nothing
End Sub