Wednesday, April 05, 2006

Ordered HashTable

Yesterday one of my colleague was asking me whether there is any ordered HashTable available in the .NET framework, as we all know HashTable is not an ordered list( like Array or ArrayList ), and so far my knowledge goes there is no such collection available which is ordered hash.

So, the probelm was like this ... consider the following code ...
Hashtable mAnotherHashTbl = new Hashtable(); mAnotherHashTbl.Add( "A", "AAA" ); mAnotherHashTbl.Add( "B", "BBB" ); mAnotherHashTbl.Add( "C", "CCC" ); mAnotherHashTbl.Add( "D", "DDD" ); mAnotherHashTbl.Add( "E", "EEE" );

and now if you loop thru the hash table like this ...
foreach( string key in mAnotherHashTbl.Keys ) { Console.WriteLine( mAnotherHashTbl[ key ].ToString()); }

you may( will ) not see AAA, BBB, CCC, DDD, EEE. in this order.

My friend apparently found a simple solution, took a HashTable and also an ArrayList and store all the keys in the ArrayList and when you have to enumerate the HashTable .. just loop thru the ArrayList and get the value from the HashTable using HashTable [ key ] .

I personally think this is the only solution available to have an ordered HashTable, we need an array/arraylist and a HashTable.
I tried to develop a class (OrderedHashTable) which encapsulate the above idea,( I have thought develop one long time back, being lazy by nature it took me this long time -:) ) so from the user's point view this collection will behave exactly like a HashTable but this it will be ordered.

Since I don't found any ways to attach the code with this blog hence I am coping the whole code base( OrderedHashTable class) as part of the blog .. which may look ugly .. if anybody feel interested I would suggest copy the code from here and paste it in a IDE and then have a look.

using System;
using System.Collections;

namespace Neo.Rnd.OrderedHashTable
{ /// /// Summary description for OrderedHashTable. ///
public class OrderedHashTable:ICollection,IDictionary
{
Hashtable m_HashObjects; ArrayList m_ArrObjects;
public OrderedHashTable()
{
m_HashObjects = new Hashtable();
m_ArrObjects = new ArrayList();
}

#region ICollection Members

public bool IsSynchronized { get { // TODO: Add OrderedHashTable.IsSynchronized getter implementation return false; } }

public void CopyTo(Array array, int index) { // TODO: Add OrderedHashTable.CopyTo implementation }

public object SyncRoot { get { // TODO: Add OrderedHashTable.SyncRoot getter implementation return null; } }

#endregion

#region IEnumerable Members

public IEnumerator GetEnumerator() { // TODO: Add OrderedHashTable.GetEnumerator implementation return null; }

#endregion

#region IDictionary Members

public bool IsReadOnly { get { // TODO: Add OrderedHashTable.IsReadOnly getter implementation return false; } }

IDictionaryEnumerator System.Collections.IDictionary.GetEnumerator() { // TODO: Add OrderedHashTable.System.Collections.IDictionary.GetEnumerator implementation return null; }

public object this[object key] { get { return m_HashObjects[ key ]; } set { // TODO: Add OrderedHashTable.this setter implementation } }

public void Remove( object key ) { m_HashObjects.Remove( key ); int index = GetIndexByKey( key ); m_ArrObjects.RemoveAt( index ); }

public bool Contains( object key ) { return( m_HashObjects.Contains( key )); }

public void Clear() { m_HashObjects.Clear(); m_ArrObjects.Clear(); }

public ICollection Values { get { ArrayList retList = new ArrayList (); for( int i = 0; i < m_ArrObjects.Count; i++ ) { retList.Add ( ((DictionaryEntry)m_ArrObjects[i]).Value ); } return retList; } }

public void Add( object key, object val ) { m_HashObjects.Add( key, val ); m_ArrObjects.Add( new DictionaryEntry( key, val ) ); }

public ICollection Keys { get { ArrayList retList = new ArrayList (); for( int i = 0; i < m_ArrObjects.Count; i++ ) { retList.Add ( ((DictionaryEntry)m_ArrObjects[ i ]).Key ); } return retList; } }

public bool IsFixedSize { get { // TODO: Add OrderedHashTable.IsFixedSize getter implementation return false; } }

#endregion

#region ICollection Members

public int Count { get { return m_ArrObjects.Count; } }

#endregion

#region Helper routines
private int GetIndexByKey( object key )
{
for( int i = 0; i < m_ArrObjects.Count; i++ )
{
object currKey = ((DictionaryEntry) m_ArrObjects[ i ]).Key;
if( currKey.Equals( key ))
{
return i;
}
}
return -1;
}
#endregion
}
}