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.
-
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 Propertyexample
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 Subjpinto3912 : 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().Delete I suggest: ii) someKeys.Add(Key, Key) allowing someKeys.Remove(vKey) later on; iii) Get Keys(anIndex as long) as Stringcmsjr : 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 .Itemcmsjr : Also, crawls in the sense of performance, or in the sense of is not complete?jpinto3912 : "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)cmsjr : 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.jpinto3912 : 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 Property I'm vba-only...cmsjr : 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.jpinto3912 : 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))cmsjr : 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.Mark Nold : Nice effort on the answer guys. @cmsjr did you ever work out how to simplify the export/import solution on NewEnum for VBA?cmsjr : 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. -
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).
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.