Generics & Sorting

T

TC

Hey All,

I've been handed a brain teaser of sorts and I'm hoping that you may be of
assistance.

Using a List or such of Generics objects, what would be the best way of
constructing one's own sorting algorithm?

For example, say we have class 'Dog' which has the properties 'Color', 'Age'
and 'Name'. How would you go about building a generic sorting algorithm
such that one could sort by any of the above properties?

Please keep in mind that using .Net's native sorting method functions would
not be allowed.

I'm trying to think of a way to build a simple bubble sort but how to make
it transparent such that a consumer of the class or algorithm can specify
the sort key for the objects based upon the object's properties?

Thanks,

TC
 
J

Jon Skeet [C# MVP]

I've been handed a brain teaser of sorts and I'm hoping that you may be of
assistance.

Using a List or such of Generics objects, what would be the best way of
constructing one's own sorting algorithm?

For example, say we have class 'Dog' which has the properties 'Color', 'Age'
and 'Name'. How would you go about building a generic sorting algorithm
such that one could sort by any of the above properties?

Please keep in mind that using .Net's native sorting method functions would
not be allowed.

Actually *using* them shouldn't be allowed, but the design is all
there - you need some way to compare two objects, without knowing
their type beforehand. In other words, you need something to ask to
compare things: IComparer<T> and Comparison<T> spring to mind.

Jon
 
M

Marc Gravell

Please keep in mind that using .Net's native sorting method functions would
not be allowed.
Why not? Is this purely academic?
I'm trying to think of a way to build a simple bubble sort but how to make
it transparent such that a consumer of the class or algorithm can specify
the sort key for the objects based upon the object's properties?

OK; what version of .NET do you have available? In .NET 3.5/C# 3, I
would be very tempted to look at using a lambda/delegate to specify
the property/properties - something similar to Jon's post here:
http://msmvps.com/blogs/jon.skeet/a...dba-expressions-don-t-work-unfortunately.aspx

Re the fundamental ordering of values - look at IComparer<T>; as a
default, Comparer<TValue>.Default is a good bet, but it would be nice
to offer custom as an argument (see my reply to Jon's blog for an
illustration).

If you don't have .NET 3.5, then consider the approach that
IBindingListView adopts for ApplySort:
http://msdn2.microsoft.com/en-us/library/e6t44cba.aspx

It isn't quite as elegant to use, but suffices. I've posted examples
on writing sorted lists several times (to this forum), but always re-
using the standard sort functionality under the bonnet... so just add
your own "sort" implementation...

Marc
 
S

sloan

You need to be careful using Reflection to do sorting.

As the author points out......it becomes a little tedious to write
EmployeeComparer
DepartmentComparer
WorkWeekComparer
etc, etc.

However, if you're sorting a butt load (<<technical term) of records...you
may want to choose performance over ease.
 
T

TC

Yeah, the only problem with that is the fact that it is not loosely coupled
(i.e. we need a generic, no pun intended, comparer that can handle any
object for sorting).

For example:

List<Person> AllPersons=new List<Person>();
List<string> Properties=new List<string>();

// We assume we pass the 3 values for the 'Person' properties
Person Person1=new Person(12, "Smith", "Tom");
AllPersons.Add(Person1);
Person Person2=new Person(15, "Jones", "Davey");
AllPersons.Add(Person2);

GenericCompare<Person> SortIt=new GenericCompare<Person>();

Properties.Add("Age");
Properties.Add("LastName");
// Sort list where first param is Generic list,
// 2nd param are any number of properties in desired sort order,
//3rd param is sort ascending
SortIt(AllPersons, Properties, true);

Has anyone seen a sort algorithm for something like the above?

Regards,

Todd
 
T

TC

Hey Jay,

The one problem, though, is that it doesn't use generics. Do you have one
that does?

-- Todd
 
T

TC

/// <summary>
/// A generic bubblesort method implementation
/// Bubblesort Explanation: http://en.wikipedia.org/wiki/Bubblesort
/// </summary>
/// <typeparam name="T">The object type populating the
list</typeparam>
/// <param name="sourceList">A list of objects</param>
/// <param name="customComparer">A custom comparer object used to
compare the list items</param>
public static void BubbleSort<T>(IList<T> sourceList, IComparer<T>
customComparer)
{
try
{
// Ensure that no null values have been provided
if (sourceList == null)
{
throw new ArgumentNullException("sourceList",
Properties.Resources.NullParam);
}
else if (customComparer == null)
{
throw new ArgumentNullException("customComparer",
Properties.Resources.NullParam);
}

for (int counter = sourceList.Count - 1; counter >= 0;
counter--)
{
// The inner loop ensures that all values are
re-compared and swapped, if necessary
for (int index = 1; index <= counter; index++)
{
if (customComparer.Compare(sourceList[index - 1],
sourceList[index]) > 0)
{
// Create a temporary holding object and swap
the order of the items
T tempListItem = sourceList[index - 1];
sourceList[index - 1] = sourceList[index];
sourceList[index] = tempListItem;
}
}
}
}
catch (System.Exception exception)
{
ExceptionHandler.HandleException(exception);
}
}

/// <summary>
/// A generic quicksort method implementation
/// </summary>
/// <typeparam name="T">The object type populating the
list</typeparam>
/// <param name="sourceList">A list of objects</param>
/// <param name="customComparer">A custom comparer object used to
compare the list items</param>
public static void QuickSort<T>(IList<T> sourceList, IComparer<T>
customComparer)
{
try
{
// Ensure that no null values have been provided
if (sourceList == null)
{
throw new ArgumentNullException("sourceList",
Properties.Resources.NullParam);
}
else if (customComparer == null)
{
throw new ArgumentNullException("customComparer",
Properties.Resources.NullParam);
}

// Call the actual quicksort algorithm
CustomObjectListSort.PrivateQuickSort(sourceList,
customComparer, 0, sourceList.Count - 1);
}
catch (System.Exception exception)
{
ExceptionHandler.HandleException(exception);
}
}
#endregion

#region Private Methods
/// <summary>
/// A generic quicksort algorithm implementation
/// Quicksort Explanation: http://en.wikipedia.org/wiki/Quicksort
/// </summary>
/// <typeparam name="T">The object type populating the
list</typeparam>
/// <param name="sourceList">A list of objects</param>
/// <param name="customComparer">A custom comparer object used to
compare the list items</param>
/// <param name="lowIndex">Lower index in the list</param>
/// <param name="highIndex">Upper index in the list</param>
private static void PrivateQuickSort<T>(IList<T> sourceList,
IComparer<T> customComparer,int lowIndex, int highIndex)
{
try
{
// Ensure that no null values have been provided
if (sourceList == null)
{
throw new ArgumentNullException("sourceList",
Properties.Resources.NullParam);
}
else if (customComparer == null)
{
throw new ArgumentNullException("customComparer",
Properties.Resources.NullParam);
}

int tempLowIndex = lowIndex + 1;
int tempHighIndex = highIndex;

// If the lowIndex is not less than the highIndex, the sort
is complete
if (lowIndex < highIndex)
{
// Create a temporary holding object for swapping the
order of the items
T tempListItem = sourceList[lowIndex];

// Loop through list until all items
// between indices have been compared
while (tempLowIndex < tempHighIndex)
{
// Keep incrementing temp indices until appropriate
discrepancy for swap is found
if (customComparer.Compare(sourceList[tempLowIndex],
tempListItem) <= 0)
{
tempLowIndex++;
}
else if
(customComparer.Compare(sourceList[tempHighIndex], tempListItem) >= 0)
{
tempHighIndex--;
}
else
{
tempListItem = sourceList[tempLowIndex];
sourceList[tempLowIndex] =
sourceList[tempHighIndex];
sourceList[tempHighIndex] = tempListItem;
}
}

if (customComparer.Compare(sourceList[tempLowIndex],
tempListItem) < 0)
{
// If tempLow is less than lowIndex, swap the items
tempListItem = sourceList[tempLowIndex];
sourceList[tempLowIndex] = sourceList[lowIndex];
sourceList[lowIndex] = tempListItem;
tempLowIndex--;
}
else
{
// Otherwise, swap item just below tempLow with the
lowIndex
tempLowIndex--;
tempListItem = sourceList[tempLowIndex];
sourceList[tempLowIndex] = sourceList[lowIndex];
sourceList[lowIndex] = tempListItem;
}

// Recursively call the quicksort function until both
partitions
// (i.e. 'lowIndex' and 'highIndex' pieces of list) are
complete
PrivateQuickSort(sourceList, customComparer, lowIndex,
tempLowIndex);
PrivateQuickSort(sourceList, customComparer,
tempHighIndex, highIndex);
}
}
catch (System.Exception exception)
{
ExceptionHandler.HandleException(exception);
}
}
 
C

Christopher Van Kirk

TC's implementation will work, but you have to provide a custom
comparer for the class. One alternative is to make your classes
inherit Comparable instead. The drawback of this approach is that you
can only sort in one order.

Your problem statement, however, implies that you want to be able to
sort by the properties intrinsically. TC's approach accomplishes this
by enabling you to design a custom comparer for any particular order
you like. An alternative is to have each of the "sortable" properties
inherit from IComparable or IComparable<>, and use reflection to
obtain handles to those properties. Reflection itself is quite slow,
but can be optimized as here: http://www.codeplex.com/Dynamic.

/// <summary>
/// A generic bubblesort method implementation
/// Bubblesort Explanation: http://en.wikipedia.org/wiki/Bubblesort
/// </summary>
/// <typeparam name="T">The object type populating the
list</typeparam>
/// <param name="sourceList">A list of objects</param>
/// <param name="customComparer">A custom comparer object used to
compare the list items</param>
public static void BubbleSort<T>(IList<T> sourceList, IComparer<T>
customComparer)
{
try
{
// Ensure that no null values have been provided
if (sourceList == null)
{
throw new ArgumentNullException("sourceList",
Properties.Resources.NullParam);
}
else if (customComparer == null)
{
throw new ArgumentNullException("customComparer",
Properties.Resources.NullParam);
}

for (int counter = sourceList.Count - 1; counter >= 0;
counter--)
{
// The inner loop ensures that all values are
re-compared and swapped, if necessary
for (int index = 1; index <= counter; index++)
{
if (customComparer.Compare(sourceList[index - 1],
sourceList[index]) > 0)
{
// Create a temporary holding object and swap
the order of the items
T tempListItem = sourceList[index - 1];
sourceList[index - 1] = sourceList[index];
sourceList[index] = tempListItem;
}
}
}
}
catch (System.Exception exception)
{
ExceptionHandler.HandleException(exception);
}
}

/// <summary>
/// A generic quicksort method implementation
/// </summary>
/// <typeparam name="T">The object type populating the
list</typeparam>
/// <param name="sourceList">A list of objects</param>
/// <param name="customComparer">A custom comparer object used to
compare the list items</param>
public static void QuickSort<T>(IList<T> sourceList, IComparer<T>
customComparer)
{
try
{
// Ensure that no null values have been provided
if (sourceList == null)
{
throw new ArgumentNullException("sourceList",
Properties.Resources.NullParam);
}
else if (customComparer == null)
{
throw new ArgumentNullException("customComparer",
Properties.Resources.NullParam);
}

// Call the actual quicksort algorithm
CustomObjectListSort.PrivateQuickSort(sourceList,
customComparer, 0, sourceList.Count - 1);
}
catch (System.Exception exception)
{
ExceptionHandler.HandleException(exception);
}
}
#endregion

#region Private Methods
/// <summary>
/// A generic quicksort algorithm implementation
/// Quicksort Explanation: http://en.wikipedia.org/wiki/Quicksort
/// </summary>
/// <typeparam name="T">The object type populating the
list</typeparam>
/// <param name="sourceList">A list of objects</param>
/// <param name="customComparer">A custom comparer object used to
compare the list items</param>
/// <param name="lowIndex">Lower index in the list</param>
/// <param name="highIndex">Upper index in the list</param>
private static void PrivateQuickSort<T>(IList<T> sourceList,
IComparer<T> customComparer,int lowIndex, int highIndex)
{
try
{
// Ensure that no null values have been provided
if (sourceList == null)
{
throw new ArgumentNullException("sourceList",
Properties.Resources.NullParam);
}
else if (customComparer == null)
{
throw new ArgumentNullException("customComparer",
Properties.Resources.NullParam);
}

int tempLowIndex = lowIndex + 1;
int tempHighIndex = highIndex;

// If the lowIndex is not less than the highIndex, the sort
is complete
if (lowIndex < highIndex)
{
// Create a temporary holding object for swapping the
order of the items
T tempListItem = sourceList[lowIndex];

// Loop through list until all items
// between indices have been compared
while (tempLowIndex < tempHighIndex)
{
// Keep incrementing temp indices until appropriate
discrepancy for swap is found
if (customComparer.Compare(sourceList[tempLowIndex],
tempListItem) <= 0)
{
tempLowIndex++;
}
else if
(customComparer.Compare(sourceList[tempHighIndex], tempListItem) >= 0)
{
tempHighIndex--;
}
else
{
tempListItem = sourceList[tempLowIndex];
sourceList[tempLowIndex] =
sourceList[tempHighIndex];
sourceList[tempHighIndex] = tempListItem;
}
}

if (customComparer.Compare(sourceList[tempLowIndex],
tempListItem) < 0)
{
// If tempLow is less than lowIndex, swap the items
tempListItem = sourceList[tempLowIndex];
sourceList[tempLowIndex] = sourceList[lowIndex];
sourceList[lowIndex] = tempListItem;
tempLowIndex--;
}
else
{
// Otherwise, swap item just below tempLow with the
lowIndex
tempLowIndex--;
tempListItem = sourceList[tempLowIndex];
sourceList[tempLowIndex] = sourceList[lowIndex];
sourceList[lowIndex] = tempListItem;
}

// Recursively call the quicksort function until both
partitions
// (i.e. 'lowIndex' and 'highIndex' pieces of list) are
complete
PrivateQuickSort(sourceList, customComparer, lowIndex,
tempLowIndex);
PrivateQuickSort(sourceList, customComparer,
tempHighIndex, highIndex);
}
}
catch (System.Exception exception)
{
ExceptionHandler.HandleException(exception);
}
}


TC said:
Hey All,

I've been handed a brain teaser of sorts and I'm hoping that you may be of
assistance.

Using a List or such of Generics objects, what would be the best way of
constructing one's own sorting algorithm?

For example, say we have class 'Dog' which has the properties 'Color',
'Age' and 'Name'. How would you go about building a generic sorting
algorithm such that one could sort by any of the above properties?

Please keep in mind that using .Net's native sorting method functions
would not be allowed.

I'm trying to think of a way to build a simple bubble sort but how to make
it transparent such that a consumer of the class or algorithm can specify
the sort key for the objects based upon the object's properties?

Thanks,

TC
 
J

Jon Skeet [C# MVP]

Christopher Van Kirk said:
TC's implementation will work, but you have to provide a custom
comparer for the class. One alternative is to make your classes
inherit Comparable instead. The drawback of this approach is that you
can only sort in one order.

If you specify Comparer<T>.Default, then if T implements IComparable it
will use that.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Top