I ran into a similar situation where I needed a hastable sorted on different values, and could not easily use a database to do the sort.
Eric Gunnerson as it turns out had published an article on MSDN for improving foreach - he wanted to have a similar facility as found in perl for sorting using foreach...
Something else that's common when dealing with data in a hashtable is wanting to order the entries based on the values in the hashtable. We'd like to be able to use the same IComparable approach as we used previously, but that takes a little more work. What we need is a class that has an IComparable implementation that looks up the hashtable values from the keys, and then compares those values.
This is done by creating another class named SortHashValueItem. An instance of this class is created for each key in the hashtable, storing the hashtable key, the hashtable reference, and an IComparer object (if applicable). This class implements IComparable, and the CompareTo() function forwards the comparison to the values in the hashtable or the IComparer.
So - I have used his code, and the code from CodeSmith to make a hashtable that has a sort method on it. Its probably terribly inefficient (time constraints, refactor when I have the opportunity) but it works.
Caveats aside - on the hashtable, which stores a collection of Participants for an event, I have created a set of sort methods for each value I want to sort on. I use Erics method of sorting using foreach, and a custom comparer to sort the hashtable into an arraylist, which is then returned.
First, I populate the collection, then generate a sorted version of it sorting on whatever parameter required by calling the appropriate method. This returns an arraylist of the contents of the hashtable sorted on in this case the last name. The arraylist can then be bound to a control etc.
ParticipantCollection registeredParticipants = ParticipantCollection.Find(m_currentOrgID, myEvent,
true);
ArrayList sortedParticipants = registeredParticipants.SortLastName();
Sort method on ParticipantCollection - uses an Eric Gunnerson IterSortHashValue class, typed to the class I am using:
#region Sort Methods
#region (Method) SortLastName
/// <summary>
/// Sorts the contents of the collection based on the last name
/// A better way of implimenting this is required.
/// </summary>
/// <returns>Returns an arraylist of participants from the collection sorted by last name</returns>
public ArrayList SortLastName()
{
ArrayList sortedItems = new ArrayList();
foreach (int participantID in new ParticipantCollectionSort(this, new ParticipantComparer()))
{
sortedItems.Add(this[participantID]);
}
return sortedItems;
}
#endregion
ParticipantComparer class:
#region (Class) ParticipantComparer
/// <summary>
/// ParticipantComparer - defines allowable comparisions to be made between participant objects.
/// This class is used to assist in sorting by defining what property to use to do the sort
/// </summary>
public class ParticipantComparer: IComparer
{
/// <summary>
/// Compare Participant objects based on the LastName property. The implimentation IComparer requires
/// type Object to be passed in. The method internally defines that Participant objects are the only
/// type allowable
/// </summary>
/// <param name="x">Participant Object</param>
/// <param name="y">Participant Object</param>
/// <returns></returns>
public int Compare(object x, object y)
{
if (!(x is Participant && y is Participant))
{
throw new ArgumentException
("The objects to compare must be of type 'Participant'");
}
string tempObj1 = ((Participant)x).LastName;
string tempObj2 = ((Participant)y).LastName;
return tempObj1.ToString().CompareTo (tempObj2.ToString ());
}
}
#endregion
ParticipantCollectionSort - from Eric Gunnerson's Iter collection code (typed again)
using System;
using System.Collections;
namespace DomainModel
{
/// <summary>
/// Iterate the keys in a hashtable, ordering them by the values
/// corresponding to those keys. Either uses the defined ordering on the
/// values or a passed-in IComparer implementation
/// </summary>
public class ParticipantCollectionSort: IEnumerable
{
internal class IterSortHashValueEnumerator: IEnumerator
{
ArrayList items = new ArrayList();
int currentItem;
internal IterSortHashValueEnumerator(ParticipantCollection hashtable, IComparer comparer)
{
// create a new SortHashValueItem for each key.
foreach (int key in hashtable.Keys)
{
SortHashValueItem item = new SortHashValueItem(hashtable, key, comparer);
items.Add(item);
}
currentItem = -1;
items.Sort();
}
public void Reset()
{
currentItem = -1;
}
public bool MoveNext()
{
currentItem++;
if (currentItem == items.Count)
return false;
return true;
}
public object Current
{
get
{
SortHashValueItem current = (SortHashValueItem) items[currentItem];
return current.Key;
}
}
}
// item to hold a key and the related hash table
internal class SortHashValueItem: IComparable
{
ParticipantCollection hashtable;
int key;
IComparer comparer;
public SortHashValueItem(ParticipantCollection hashtable, int key, IComparer comparer)
{
this.hashtable = hashtable;
this.key = key;
this.comparer = comparer;
}
public object Key
{
get
{
return key;
}
}
public int CompareTo(object object2)
{
SortHashValueItem item2 = (SortHashValueItem) object2;
// get the two values for these keys...
object value1 = hashtable[key];
object value2 = item2.hashtable[item2.key];
if (comparer != null)
{
return comparer.Compare(value1 ,value2);
}
else
{
// compare one to another. They must implement IComparable...
IComparable comparable = (IComparable) value1;
return comparable.CompareTo(value2);
}
}
}
/// <summary>
/// Create an instance of the IterSortHashValue class
/// </summary>
/// <param name="hashtable">The hashtable to use</param>
public ParticipantCollectionSort(ParticipantCollection hashtable)
{
this.hashtable = hashtable;
}
/// <summary>
/// Create an instance of the IterSortHashValue class, using a
/// specific comparer
/// </summary>
/// <param name="hashtable">The hashtable to use</param>
/// <param name="comparer">The comparer to use</param>
public ParticipantCollectionSort(ParticipantCollection hashtable, IComparer comparer)
{
this.hashtable = hashtable;
this.comparer = comparer;
}
public IEnumerator GetEnumerator()
{
return new IterSortHashValueEnumerator(hashtable, comparer);
}
ParticipantCollection hashtable;
IComparer comparer;
}
}