ideal way of clearing out collections - vba

In my procedure, I have multiple collections that are templates for data. These collections will be stocked with data, called upon, and then emptied for repopulation during my repetitive process. I've read that I can do: set myCollection = new Collection to essentially clear it out for reuse.
I've also seen set myCollection = Nothing. Is there a preferred method for handling the clearing out of existing collections?

I have extracted related portion from an article which clarifies this point.
Use Set means the collection will behave differently than when you set the collection to nothing. The next section explains this.
Removing All items from a Collection
To remove all items from a collection you can simply set it to nothing.
Set Coll = Nothing
An important point to understand here is that what this does depends on how you created the collection. As we saw you can create a Collection by declaring using New or by using Set and New. Let’s look at both types
Declaring Using New
If you set this collection to nothing then it will be set to the state where the “object is not set”. When you add a new item VBA automatically sets the Collection variable to a valid collection.
In other words, if you set the collection to nothing it will empty all the items. If you then add an item to the collection you will now have a collection with one item. This makes it simple to empty a collection.
The following code demonstrates this.
Sub EmptyColl()
' Create collection and add items
Dim coll As New Collection
' add items here
' Empty collection
Set coll = Nothing
' Add item
coll.Add "Pear"
End Sub
A subtle point to emphasize here is that when you set the collection to Nothing it is not actually set to nothing. Therefore if you try to compare it with being it will not work.
Using Set and New
When you use Set to create a collection you must create the collection again if you set it to Nothing. In the following code after setting to nothing you must then set using new again. If you don’t do this you will get the error: “Object Variable or With block variable not set”.
Sub EmptyCollSet()
' Create collection
Dim coll As Collection
Set coll = New Collection
' Add items here
' Empty collection
Set coll = Nothing
' SET TO NEW BEFORE USING
Set coll = New Collection
' Add item
coll.Add "Pear"
End Sub
Remove All – An Alternative Method
The following method will also remove all the elements of a collection but is a slower way to do it. The advantage is that is will work no matter which way you create the collection.
Sub RemoveAll(ByRef coll As Collection)
Dim i As Long
For i = coll.Count To 1 Step -1
coll.Remove i
Next i
End Sub
For details please refer The Ultimate Guide To Collections in Excel VBA by Paul Kelly

Related

Find Item.index after using Items.find

How do you look up the index of a contact item that was set using the items.find method? After finding the item, I want to be able to move to the next item, but my code sends me to the first item in the collection. A condensed version of my plan is below...
dim ColItms as items
dim CI as contactItem
Dim CIindex as integer
set CI= ColItms.find("[CompanyName] = ""IBM""")
CIindex = CI.???? ''''' This shows what I'm wanting to do, but don't know how
' now advance to next item in collection
set ci = ColItms.item(CIindex +1) ' i think this would work if I could find CIindex
set ci = ColItms.GetNext ' this fails as it returns the 1st item in the collection
Right now all that seems to work is to loop through each item in the collection to see if it matches the found contact,
Items have no intrinsic index, only an entry id.
To find the next match, use Items.FindNext.

How to get an object out of a collection in VBa

I used to have the following working code
Set result = DecodeJson(MyRequest.responseText)
Dim keys() As String
keys = GetKeys(result.issues)
Now, my approach has changed and instead of having result being the object, I receive a Collection based upon Array of class objects as class member in VBA
Dim resultsFromQueries As Collection
Set resultsFromQueries = GetAllJSonObjects(searchString) ' this calls DecodeJson(MyRequest.responseText) as per the code snippet above
Dim i As Integer
For i = 0 To resultsFromQueries.Count
Dim keys() As String
Dim item As Object
Set item = resultsFromQueries.item(i + 1) ' I guess it's not 0 based?
keys = GetKeys(item.result.issues) 'KABOOM
The issue I have now is I keep getting the following exception
Run time error '424':
Object required
Checking in the watch window, item shows as type Variant/String and has the value of "[object Object]"
Do I need to cast it?
I had that problem just a few weeks ago, the problem was the following:
The collection was collected with the follwing code:Collection.Add(OBJECT)
This however leads to the addition of the value of the Object into the Collection and can be solved as simple as leaving the parathesis.Collection.add OBJECT
So my advice is to go into the GetAllJSonObjects(searchString) function and
search for the addition process and make sure that it is without parethesis.
Like this the Collection will contain Objects and not variants and your code with item(i+1) should work properly.
Good luck!

Is a VBA object destroyed when the variable is set to a new object?

I haven't found an answer specific to this question so hopefully someone can clear it up for me.
I understand the VBA Garbage Collector uses a reference count to determine if an object is not longer required, and to explicitly disassociate a variable (thereby decrementing the reference count) you use:
Set objectVariable = Nothing
Here is what I have in a spreadsheet I'm working on right now:
Declare Function GetObject Lib "ObjectCreator.dll" () As Object
Public MyObject as Object
Sub MyMethod()
Set MyObject = GetObject()
...do stuff with MyObject...
Set MyObject = GetObject()
...do stuff with my new MyObject...
Set MyObject = GetObject()
...do stuff with my even newer MyObject...
Set MyObject = Nothing
End Sub
My question is: Do all three of the created objects get destroyed by the GC or only the last one? i.e. Does the reference count of an object get decremented when its referencing variable is set to another object rather than being set to Nothing?
When you assign an object reference to a variable, the reference count goes up by one, and when the variable loses the reference by some other assignment, the object's reference count goes down by one. For example:
Dim a As Collection
Set a = new Collection 'The reference count of this new Collection object is 1
Set b = a 'The reference count of the Collection object is now 2
Set b = new Collection 'The reference count of the original collection has gone back down to 1 because b is no longer a reference to it, and this new Collection has a reference count of 1
Set a = Nothing 'The original collection no longer has any active references, so VBA safely GCs it.
Set b = Nothing 'The newer collection now no longer has any active references either, so VBA safely GCs it.
Now, in your case you're talking about an external DLL, which may manage its own memory or running state differently internally. But the way that VBA handles COM reference counts is the same.

Updating child collection of POCO (adding/updating/deleting) in Entity Framework 4.1

I have a webpage with a form that is used to edit some object. This object contains a Collection of other objects defined like this:
Public Overridable Property Employees As List(Of Employee)
On a form I can delete an employee, add a new one or modify existing one. When I click save new values are sent to the server. On a server I check if the user exists. If exists then I modify its values, if it does not exist then I add it. All employees that exist on the server and were not sent are marked as deleted (State changed to EntityState.Deleted). I try to use the following code (dbCollection = database entities, newCollection = collection sent from the form):
For Each item In dbCollection
Dim dbItem = item
Dim newTask = newCollection.FirstOrDefault(Function(i) i.Id = dbItem.Id)
If newTask Is Nothing Then
Me.Entry(item).State = EntityState.Deleted
Else
Me.Entry(item).CurrentValues.SetValues(newTask)
newCollection.Remove(newTask)
End If
Next
For Each item In newCollection
dbCollection.Add(item)
Next
Me.SaveChanges()
This code does not work, because changing to EntityState.Deleted removes the object from collection, and for each loop breaks, since the collection is modified...
I know that I can overcome this problem by using a for loop or adding objects to delete to some other list first, but I hope maybe there is a pattern that would make my code nicer.
Thanks in advance for all suggestions.
Replace...
For Each item In dbCollection
...by:
For Each item In dbCollection.ToList()
ToList() will create a copy of the collection (only the references, not the objects themselves). dbCollection.ToList() is another collection than dbCollection so that you safely can modify the dbCollection without getting the "collection has been modified" exception in the For Each loop.

Setting the Item property of a Collection in VBA

I'm surprised at how hard this has been to do but I imagine it's a quick fix so I will ask here (searched google and documentation but neither helped). I have some code that adds items to a collection using keys. When I come across a key that already exists in the collection, I simply want to set it by adding a number to the current value.
Here is the code:
If CollectionItemExists(aKey, aColl) Then 'If key already has a value
'add value to existing item
aColl(aKey).Item = aColl(aKey) + someValue
Else
'add a new item to the collection (aka a new key/value pair)
mwTable_ISO_DA.Add someValue, aKey
End If
The first time I add the key/value pair into the collection, I am adding an integer as the value. When I come across the key again, I try to add another integer to the value, but this doesn't work. I don't think the problem lies in any kind of object mis-match or something similar. The error message I currently get is
Runtime Error 424: Object Required
You can't edit values once they've been added to a collection. So this is not possible:
aColl.Item(aKey) = aColl.Item(aKey) + someValue
Instead, you can take the object out of the collection, edit its value, and add it back.
temp = aColl.Item(aKey)
aColl.Remove aKey
aColl.Add temp + someValue, aKey
This is a bit tedious, but place these three lines in a Sub and you're all set.
Collections are more friendly when they are used as containers for objects (as opposed to containers for "primitive" variables like integer, double, etc.). You can't change the object reference contained in the collection, but you can manipulate the object attached to that reference.
On a side note, I think you've misunderstood the syntax related to Item. You can't say: aColl(aKey).Item. The right syntax is aColl.Item(aKey), or, for short, aColl(aKey) since Item is the default method of the Collection object. However, I prefer to use the full, explicit form...
Dictionaries are more versatile and more time efficient than Collections. If you went this route you could run an simple Exists test on the Dictionary directly below, and then update the key value
Patrick Matthews has written an excellent article on dictionaries v collections
Sub Test()
Dim MyDict
Set MyDict = CreateObject("scripting.dictionary")
MyDict.Add "apples", 10
If MyDict.exists("apples") Then MyDict.Item("apples") = MyDict.Item("apples") + 20
MsgBox MyDict.Item("apples")
End Sub
I think you need to remove the existing key-value pair and then add the key to the collection again but with the new value