BradVin's .Net Blog

Code, snippets, controls, utils, etc. Basically all things .net

UberUtils - Part 4 : Collections

 ÜberUtils Series posts so far :

I figured it was about time I did another post in the UberUtils series seeing that I haven't written a post in over a month. So here is the next installment. This time it's for collections. I hear you asking "but the framework has every collection I will ever need", and this is true for 98% of the time. But every once in a while there comes a time where I find myself subtly changing the way an existing collection works, or wishing it could do something just slightly differently. As with any software development, there are many different ways to solve the same problem, so you probably find yourself getting around limitations with the built-in collections without even knowing it. (I know I have when I go back and look at code I've written).

The number one question I have found myself asking over the years with regards to the collection namespace is:

Is there a dictionary whereby I can access the values from both the key and an index?

The answer to this is NO. And if I'm wrong about this then please shoot me now for wasting so much time over the years on this code. You can overcome this limitation by using the other collections at your disposal, and with not so much code either. The thing is, I hate writing the same code over and over again (I actually refuse to do it), so why not write a generic dictionary that can access the values by index? And then just use that dictionary instead of the normal dictionary in the future.

Introducing the IndexedDictionary, an indexable dictionary. Here is a unit test to show the indexing feature :

        [TestMethod()]
public void SomeTest()
{
IndexedDictionary<string, int> col = new IndexedDictionary<string,int>();
col.Add("1", 1);
col.Add("2", 2);
col.AddAt(0, "0", 0); //add by index
col.Add("3", 3);
col.RemoveAt(2); //remove by index
string strList = string.Empty;
for (int i = 0; i < col.Count; i++)
{
strList += col[i]; //get by index
}
Assert.AreEqual(strList, "013");
}
As you can see, you can access the dictionary now by an index (aswell as your key). This is accomplished simply by inheriting from the Dictionary class and holding a List inside the class too :
    public class IndexedDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
protected List<TKey> m_col = new List<TKey>();

I have also added some extra features which can be toggled :

  • Replace Duplicate Keys - obviously you cannot have multiple entries in a dictionary with the same key, so what this does instead is replaces a value with the same key when adding to the collection
  • Throw Error On Invalid Remove - if disabled, when you try to remove an item that does not exist in the collection it just does nothing and does not throw an exception as it normally does

Immediately after this new collection came the NamedIndexedDictionary. This is a sub class of the IndexedDictionary class that takes a string as the key.

public class NamedIndexedDictionary<TValue> : IndexedDictionary<string, TValue>

The string key can also be set to be case insensitive or not, so calling col["abc"] is the same as col["ABC"].

Both have been very useful to me in the past and I hope someone else will find them useful too. Send me any other helpful collections you have created and I will add them too. 

Download here : collections.zip

 

Published Thursday, December 13, 2007 12:30 PM by bradvin
Filed under: , , ,

Comments

# re: UberUtils - Part 4 : Collections@ Monday, December 17, 2007 1:49 AM

"Is there a dictionary whereby I can access the values from both the key and an index?

The answer to this is NO. And if I'm wrong about this then please shoot me now for wasting so much time over the years on this code. "

See:

System.Collections.Specialized.NameObjectCollectionBase

Bang!

by BEM

# re: UberUtils - Part 4 : Collections@ Monday, December 17, 2007 5:28 AM

[undeleted]

Richard wrote : 

I think it's usually called a bi-directional map. Here's Google's implementation of it, in Java.

google-collections.googlecode.com/.../BiMap.java

by bradvin

# re: UberUtils - Part 4 : Collections@ Monday, December 17, 2007 5:36 AM

@BEM

lol - ok u got me & thanks for pointing that class out. BUT although the NameObjectCollectionBase does exist, it's abstract and not generic. It's also set at using a string as the key, which is almost identical to my NamedIndexedDictionary, so maybe it could replace mine, but not without writing quite a bit of code first.

by bradvin

# re: UberUtils - Part 4 : Collections@ Tuesday, December 18, 2007 1:50 PM

Take a look at System.Collection.ObjectModel.KeyedCollection.  It is close to what you want.  It is addressable by index or key of object.  Yes the class is abstract but it only requires that you implement 1 method to get it working, GetKeyForItem(TItem).  The draw back that I've seen is that it uses it's indexer (collection[i]) to address both the keyed and the indexed value so that means your key can't be an int.

It uses a hybrid method of looking up keys in the collection.  If the set is suffeciently small ( < dictionaryCreationThreshold ) it will iterate over the collection checking the value being queried for to GetKeyForItem() of the current item in the iteration.  However if it is exceeds that threshold it stores the keys in a dictionary.  The threshold is definable when you create the collection.

John

by John Sheppard

# re: UberUtils - Part 4 : Collections@ Friday, October 24, 2008 7:48 PM

I wrote one of these for the .Net 1.1 framework and used it quite a bit.  I'm very happy to see one that uses Generics!

Thanks,

Johnny

# re: UberUtils - Part 4 : Collections@ Thursday, October 30, 2008 2:07 PM

Hi Brad,

I have been using your dictionary and would like to thank you for it. Here are a few things:

1. The clear() function inherited from the dictionary class clears the dictionary but  not the list.

2. Events (for Add, Remove and Clear) are a usefull addition to such a collection (I wonder why Microsoft does not have them).

3. The collection goes into a dark spot when used with an integer key (when retriving a value it is unclear if it shoud be returned by the key or the index). I think this case should be prevented with an Exception.

4. When assigning a value to a non existing key (as in: IndexDic[newKey]=value), the value is added to the dictionary and the lkey is not added to the list.

5. Would it be OK with you if I published a version of the collection with the above fixed in CodePlex (Giving you credit)?

6. How about commercial use? Do you allow it?

Best,

Asher Barak

asher.barak@gmail.com

by Asher

# Squashing the asp.net MVC response - part 1@ Sunday, April 12, 2009 6:10 PM

The goal of this post : reduce the total size of a simple asp.net MVC page response. Our measuring tools

Leave a Comment

(required) 
(required) 
(optional)
(required)