kateroh
kateroh

Reputation: 4406

Multi-key dictionary with optional keys

I have a need for a dictionary with multiple keys of 2 different types (int and string, both unique, so they can appear only inside of 1 key). Here is an example: group information (GroupInfo) can be queried by either GroupdId or one of the member names:


GroupId   MemberNames           GroupInfo
{1,       John, Mary, Joe} ==>  {GroupInfo}

So group info should be returned when requested by either id (1) or one of the member names (John).

My first solution was to create a key that wraps GroupdId and MemberNames with overridden Equals method that compares GroupIds and looks up a list of members. However to make these entries equal:


GroupId   MemberNames          
{0,       John}
{1,       null}
{1,       Mary}

GetHashCode has to return the same const value. This will result in a dictionary becoming a linked list and performance degrading to O(N) lookup in the best case scenario.

The other solution is to keep 2 dictionaries separately: GroupId ==> GroupInfo, MemberName ==> GroupInfo.

Any other ideas?

Upvotes: 3

Views: 1791

Answers (2)

Philipp Schmid
Philipp Schmid

Reputation: 5828

In order to maintain only 1 dictionary, consider converting the GroupId (int) into a string and use it as a key (number 'keys' should not conflict with name keys). Maintain a references to the keys, so that if one gets deleted, the rest will be deleted.

Upvotes: 1

DevinB
DevinB

Reputation: 8316

Based on what you described in your comment

how'd you delete by a key? For example given a key "John" all other keys should be deleted as well.

It may have become clear to you now that a "Dictionary" isn't what you are looking for. Mostly because you have a need for multiple key types, and a need to map keys to other keys.

So you can create your own class that implements IDictionary. Basically as follows.

    class MultiKeyDictionary : IDictionary
{
    Dictionary<string, GroupInfo> stringDict = new Dictionary<string, GroupInfo>();
    Dictionary<int, GroupInfo> intDict = new Dictionary<int, GroupInfo>();
    Dictionary<GroupInfo, List<object>> keysDict = new Dictionary<GroupInfo, List<object>>();

    //Each of these would add to their own dictionary, as well as adding the backwards
    //entry in the "keysDict"
    public void Add(string memberName, GroupInfo value);
    public void Add(int key, GroupInfo value);

    public bool Contains(string key);
    public bool Contains(int key);

    //This would be the enumerator of the "keys" of "keysDict"
    //because it is actually a list of all GroupInfos
    public IDictionaryEnumerator GetEnumerator()

    public ICollection NameKeys;
    public ICollection GroupIDKeys;
    //This is to adhere to the interface. It should be carefully commented or even deprecated.
    public ICollection Keys;

    //For this, you look up the GroupInfo for the key, then do
    //foreach(object key in keysDict[<groupInfoIJustLookedUp>]) {
    //   if(key.gettype == typeof(string) stringDict.Remove(key);
    //   else if (key.gettype == typeof(int) intDict.Remove(key);
    //   else //WHAT?!?
    //}
    public void Remove(string key);
    public void Remove(int key);

    //This would be the "Keys" collection of the "keysDict"
    public ICollection Values;

    //etc... etc...
    public object this[string memberName];
    public object this[int groupId];
}

Upvotes: 4

Related Questions