tags:

views:

767

answers:

2

The environment is that the members I'm pushing into the Collection are nameless, un-identifiable (to avoid bad abstractions, and please, don't freak out: members are actually other Collection instances). In order to be able to make fast searches, I'm creating a meaningfull hash name for each new member, and provide it as the Key string, on the Add method of the "topmost" Collection.

When I have a key to seach with, everything's dandy... Problem is I'd like to iterate the members of the collection and get the Key that was provided on Add (the generated Hash, that unfortunetely is not possible to reverse-Hash).

I'm moving on by defining that the first member of the inserted sub-collection instance is a string, containing the mentioned hash, but if anyone cracks this, I'll be much obliged.

+2  A: 

The simple approach would be to use a Dictionary instead of a Collection. The Dictionary is essentially an associative array of key, item pairs and support retrieval of its keys as an array. To use the Dictionary you will need to add a reference to the Microsoft Scripting Runtime. The drawback to using the dictionary is that it is not enumerable in the same way as the collection. A more elaborate solution would be to wrap the collection and dictionary to create an enumerable dictionary as outlined below.

NB To get NewEnum to work properly in VBA the class module has to be exported and manually edited as follows and then re-imported.

Public Property Get NewEnum() As IUnknown
Attribute NewEnum.VB_UserMemId = -4
   Set NewEnum = someKeys.[_NewEnum]
End Property

example

Option Explicit
Private someKeys As Dictionary
Private someCols As Collection
Public Function Add(o As Object, Key As String) As Object
    someKeys.Add Key, o
    someCols.Add o, Key
End Function
Public Property Get Count() As Long
    Count = someCols.Count
End Property
Public Property Get Item(vKey As Variant) As Object
    Set Item = someCols.Item(vKey)
End Property
Public Sub Remove(vKey As Variant)
    someKeys.Remove vKey
    someCols.Remove vKey
End Sub
Public Property Get NewEnum() As IUnknown
   Set NewEnum = someCols.[_NewEnum]
End Property
Public Property Get Keys() As Variant
    Keys = someKeys.Keys
End Property
Private Sub Class_Initialize()
    Set someKeys = New Dictionary
    Set someCols = New Collection
End Sub
cmsjr
It crawls. There are Buts: i) Key can't be optional on Add method; ii) how do you remove the corresponding key during remove method?; iii) it's dumb-possible to aC.Keys().DeleteI suggest: ii) someKeys.Add(Key, Key) allowing someKeys.Remove(vKey) later on; iii) Get Keys(anIndex as long) as String
jpinto3912
i. good point. ii. I didn't give that any consideration, and your suggestion is the better of the two alternatives offered, but Key, Key irks me. Maybe Dictionary would be better for the keys. iii. That Get would be redundant since the default member of the collection returned would be .Item
cmsjr
Also, crawls in the sense of performance, or in the sense of is not complete?
cmsjr
"crawls" in correcteness (but it's on polish). I think I didn't came across on iii)If you're giving a ref to a priv. member, I guess it gives a user the opport. to mess with it (don't know if vba errors). E.G. set myKeys=thisWrapCol.Keys() Call myKeys.Delete()Agree on ii? (yeah it's weird)
jpinto3912
Ok, I'm with you on iii. For ii I still wonder if it wouldn't make sense to make someKeys a Dictionary, add the key and collection during Add and expose its Keys collection through a property, this would give you a way to get the set of Keys or an individual Key without encapsulation issues.
cmsjr
so iii is addressed, and the dictionary can remove by key or index, so ii is addressed as well.
cmsjr
I guess that now I lost you on iii)... are you re-editing to not pass the priv member?Can't chew Public Property Get NewEnum() As IUnknown Set NewEnum = someCol.[_NewEnum]End PropertyI'm vba-only...
jpinto3912
Not totally, I'm with you on iii if we use two collections, I'm just suggesting that may not be the best way. If you can't support the NewEnum in VBA I don't think you will be able to do a foreach iteration over its members. Does that present a problem?
cmsjr
Give me a few minutes and I'll edit the sample to clarify what I'm suggesting now.
cmsjr
I think there is a workaround for the NewEnum, but I'll have to do more research. I'm sans IDE at the moment, so please pardon any syntax issues.
cmsjr
Note that the keys collection of the dictionary being returned by the Get is read only.
cmsjr
I'm waiting for a process to finish on this machine before I can fire up my work computer, but I think I am going to try wrapping the Dictionary and seeing if I can get the new enum to work in a VBA friendly way, that would remove the need for two aggregate objects.
cmsjr
Dictionary requires a reference to Microsoft Scripting Runtime, is that a deal breaker?
cmsjr
Ok sample reworked above, NewEnum about note is up there. I verified that assignment will not alter the dictionaries keys collection, or an individually indexed key. So I think exposing the read only attribute of the private member should be safe.
cmsjr
hihihi... why have a Collection encaps. a Dic., when the Dictionary alone solves all our problems? I can Add whatever I want to a Dictionary (in my case, adding collections), and this class has property Keys. I just didn't know this class (it's usable in vba, checked).
jpinto3912
So, please, trim down all the prev. answer, give us a lesson on Dictionary (not to me, I'm already using it thanks to you) and i'll be happy to mark this answer correct.Nice work! Thanks !
jpinto3912
Personal taste:"using a Dictionary". BTW, don't forget to tell the kids that Keys array stars at 0.
jpinto3912
Let's polish that other thought: we can enumerate by myDic(myDic.Keys(enum_index)). But we could also wrap a Dictionary and provide a P. Prop. Get Enum(anIndex as long) as Object, (code to figure out whether object or variant.. and anIndex is withibounds) set Enum = innerDic(innerDic.Keys(anIndex))
jpinto3912
If we need to pass it an index then that Enum would just duplicate our classes Item method (though I agree some logic to make sure an invalid key/index doesn't cause an exception might be in order) The real point of wrapping them together is to support for each syntax.
cmsjr
Surely, the kids know that arrays are 0 base.
cmsjr
Nice effort on the answer guys. @cmsjr did you ever work out how to simplify the export/import solution on NewEnum for VBA?
Mark Nold
If one has access to the Visual Studio 6 IDE, it can be coded normally in VB6 then imported into your VBA project. Not sure if there's any other way to handle it.
cmsjr
A: 

You could wrap your member collections in you own collection call that store the key and the collection. The is just a matter of iterating throw the collection and asking the elements for their key (i.e. you hash).