Jeffrey Palermo (.com)

Sponsors

The Lounge

News

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Sorting in .Net (don't sort manually, let .Net do it for you) - level 100

I've labeled this as a level 100 topic because I feel that every .Net developer should know the basics of sorting.  With the base class library that we have to work with, we don't have to worry about sorting algorithms or speeding up our sorts.  Folks smarter than I have implemented the fast sorting that we have in .Net.

Here, I'll cover the basics of how to sort your own domain object as well as how to sort any other object (even ones you didn't create).

Let's consider the following domain object, Person:

   11 public class Person

   12 {

   13     private string _firstName;

   14     private string _lastName;

   15 

   16     public Person(string firstName, string lastName)

   17     {

   18         _firstName = firstName;

   19         _lastName = lastName;

   20     }

   21 

   22     public string FirstName

   23     {

   24         get { return _firstName; }

   25     }

   26 

   27     public string LastName

   28     {

   29         get { return _lastName; }

   30     }

   31 }


In our application, we may want to display FirstName LastName or LastName, FirstName.  Also, we may want ascending or descending sorts.  If you are using a database, you may be tempted to let the database order the records for you, and that's fine if you never need to change the sort inside the application.  In object-oriented systems, however, a lot of code never talks to the database, so we need to be able to sort.  Now, suppose I wanted the following code to work:

 

[Test]

public void ShouldSortPersonsAscending()

{

    Person[] persons = new Person[]{new Person("Homer", "Simpson"), new Person("Bart", "Simpson"), new Person("Jerry", "Seinfeld")};

    Array.Sort(persons);

    foreach(Person person in persons)

    {

        Console.WriteLine(person.FullName);

    }

}


This code won't work right now because Array.Sort( ) counts on the elements in the array implementing the IComparable interface.  This is a key interface in .net for sorting.  By adding an implementation of that method to my Person class, I get sorting for free.  What I am going for here is a sort by last name and then first name:

public class Person : IComparable

{

    private string _firstName;

    private string _lastName;

 

    public Person(string firstName, string lastName)

    {

        _firstName = firstName;

        _lastName = lastName;

    }

 

    public string FirstName

    {

        get { return _firstName; }

    }

 

    public string LastName

    {

        get { return _lastName; }

    }

 

    public string FullName

    {

        get { return string.Format("{0} {1}", FirstName, LastName); }

    }

 

    public int CompareTo(object obj)

    {

        Person person = (Person)obj;

        int compareResult = LastName.CompareTo(person.LastName);

        if(compareResult == 0)

        {

            compareResult = FirstName.CompareTo(person.FirstName);

        }

 

        return compareResult;

    }


Notice how I just had to implement a single method comparing first on last name and then on first name.  Here is my output:

------ Test started: Assembly: BlogScratchPad.dll ------

Jerry Seinfeld
Bart Simpson
Homer Simpson

1 passed, 0 failed, 0 skipped, took 0.41 seconds.

That's the bread and butter sorting case, and that gives my Person object a default sort order.  Anyone working with Person(s) can now sort by last name, first name in ascending order.  But what if I wanted to sort in descending order?  There is another key interface in .Net called IComparer.  This interface enables you to create a class that defines the sort order for any class.  Consider the following tests.  I would like my Person(s) to be listed in descending alphabetical order (reverse of above).  Here, I choose to implement the generic form to avoid some casting: IComparer<T>.

[Test]

public void ShouldSortPersonsDescending()

{

    Person[] persons = new Person[] { new Person("Homer", "Simpson"), new Person("Bart", "Simpson"), new Person("Jerry", "Seinfeld") };

    Array.Sort(persons, new ByLastNameFirstNameDescendingComparer());

    foreach (Person person in persons)

    {

        Console.WriteLine(person.FullName);

    }

}


Notice how this test looks very similar to the previous one; however, I've added my new comparer class to instruct Array.Sort( ) how to sort the array.

public class ByLastNameFirstNameDescendingComparer : IComparer<Person>

{

    public int Compare(Person x, Person y)

    {

        int compareResult = y.LastName.CompareTo(x.LastName);

        if (compareResult == 0)

        {

            compareResult = y.FirstName.CompareTo(x.FirstName);

        }

 

        return compareResult;

    }

}


Notice how I just compared y to x instead of x to y.  This gets me the descending order that I want.  Here is the result of running the test:

------ Test started: Assembly: BlogScratchPad.dll ------

Homer Simpson
Bart Simpson
Jerry Seinfeld

1 passed, 0 failed, 0 skipped, took 0.41 seconds.

Notice how it is exactly the reverse of the previous result - just what I wanted. 

Array.Sort(Array, IComparer) works to sort an array of any object under the sun.  My team's Visual Studio solution has accumulated  a healthy number of IComparer classes.  This allows our application to sort objects however needed, whenever needed. 

Posted Wed, Nov 8 2006 7:00 AM by Jeffrey Palermo
Filed under: ,

[Advertisement]

Comments

Harris wrote re: Sorting in .Net (don't sort manually, let .Net do it for you) - level 100
on Wed, Nov 8 2006 10:04 AM
Jeffrey, It always got under my skin that if I needed to modify the comparison, I was required to create yet another ICompare implementation to do so; it just seemed like code bloat having all those comparers around. I'm not sure if you can use this in your applicaiton, but you might want to take a look at the Comparison delegate (System.Comparison). Both the Array and List objects have overloaded Sort methods that accept a Comparison argument. My thought is that you could either expose a method off your domain object to get the appropriate comparison implementation. That seems more elegant to me. You could still implement IComparable to provide basic functionality as well. Just my 2 cents...
Jeffrey Palermo wrote re: Sorting in .Net (don't sort manually, let .Net do it for you) - level 100
on Wed, Nov 8 2006 10:45 AM
Harris, Funny you should mention that. It'll be the topic of my next blog post! I do like the extensions .Net 2.0 has made in this area. It makes it very easy to work with. As far as "code bloat" because of creating more classes, I don't see it that way. I tend to prefer an IComparer over a comparison because it's easy to test. If I just inject the comparison, then I have to test it inside the context of something larger, and that complicates things.
JH wrote re: Sorting in .Net (don't sort manually, let .Net do it for you) - level 100
on Wed, Nov 8 2006 11:21 AM
This works if you are only concerned with sorting people using the default comparison implemented within a class (in this case, last name) . David Hayden has a good description of how to implement IComparer so you can roll your own sorting routines and decorate the class with sorting on whatever you want. http://codebetter.com/blogs/david.hayden/archive/2005/03/06/56584.aspx In this case, I may wish to sort based on first name instead of just the default last name sorting implemented in the Peson class, so I can roll my own class that implements IComparer (or IComparer) and decorate the Person class to sort on whatever I want.
Casimir wrote re: Sorting in .Net (don't sort manually, let .Net do it for you) - level 100
on Wed, Nov 8 2006 12:50 PM
I think that I will use LINQ to query the array with Orderby instead using the Sort() function of the array which constraints array's elements to implement IComparable or using Comparer. But If u're only concerned by C# 2.0 or 1.1, you can do it as u do by implementing IComparable or by using IComparer. Excuse for my English!
Johan wrote re: Sorting in .Net (don't sort manually, let .Net do it for you) - level 100
on Wed, Nov 8 2006 1:25 PM
Hi Jeffrey, Nice article but not very flexile and in my opinion its not the responsibility of the domain to know how to sort itself. Most of the time i use something like the following. using System; using System.Collections; namespace Sorting { public class ArraySort : IComparer { public enum SortType { Ascending, descending } private string property; private SortType order = SortType.Ascending; public ArraySort(String property, SortType order) { this.property = property; this.order = order; } public int Compare(object x, object y) { string icx = x.GetType().GetProperty(Property).GetValue(x, null).ToString(); string icy = y.GetType().GetProperty(Property).GetValue(y, null).ToString(); if (this.order == SortType.Ascending) return icx.CompareTo(icy); else return icy.CompareTo(icx); } } } If you would use the Person class and the array from above sorting it could be done in the following way: Array.Sort(persons, new ArraySort("FirstName", ArraySort.SortType.Ascending)); By the way i read your stuff with great joy. Keep up the good work!
Jeffrey Palermo wrote re: Sorting in .Net (don't sort manually, let .Net do it for you) - level 100
on Wed, Nov 8 2006 1:36 PM

Johan,

That's an interesting way to sort any class by any property.  What is your testing experience with that method?

Johan wrote re: Sorting in .Net (don't sort manually, let .Net do it for you) - level 100
on Wed, Nov 8 2006 1:37 PM

Oops Sorry. The formatting is not quite the way i expected it.

Johan wrote re: Sorting in .Net (don't sort manually, let .Net do it for you) - level 100
on Wed, Nov 8 2006 1:41 PM

Hi Jeffrey,

I have no experience with testing it. But i've used it several times but that's no proof.

The only problem is that you cannot sort on properties of associations.

Milan Negovan wrote re: Sorting in .Net (don't sort manually, let .Net do it for you) - level 100
on Wed, Nov 8 2006 8:24 PM

This is exactly the approach we use in our product, i.e. we have a comparer for each field of each domain class. This way you always know we can sort on any field, ascending or descending.

As to code bloat, it's not really a concern because these things should be code gen'ed, IMHO. You can go nuts writing comparers by hand. :)

Sam Smoot wrote re: Sorting in .Net (don't sort manually, let .Net do it for you) - level 100
on Wed, Nov 8 2006 8:39 PM

Great post! I love back-to-basics!

I wrote a response that might interest you that gives an example of how to do this inline with a single line of code and no extra classes. I'd post it here, but then I'd be giving away the punch-line. :-)

Lionel wrote re: Sorting in .Net (don't sort manually, let .Net do it for you) - level 100
on Thu, Nov 9 2006 8:18 AM

Unfortinatly Array.Sort isn't the be all and end all of sorting for example if you want a stable sort which is an uncommon requirment but ocasionaly useful you still have to code sorting yourself or am i missing a sneeky class in the class libary somewhere :).

nczimm wrote re: Sorting in .Net (don't sort manually, let .Net do it for you) - level 100
on Wed, Dec 20 2006 4:51 PM

In the properties window for the binding source of a data grid, the property "sort" has the following description " Indicates names of database columns used to sort the set of rows returned by the data source". If I set this sort property to any ciolumn in the data grid it blows up. It is obvious from this discussion that no one uses this for sorting data. I am more than a little puzzled at it's purpose. Can you help?

Jeffrey Palermo wrote re: Sorting in .Net (don't sort manually, let .Net do it for you) - level 100
on Wed, Dec 20 2006 5:11 PM

nczimm,

I'm not sure about your data grid?  WinForms?  ASP.NET?  Either way, I would sort my domain objects before binding them.  I use the Infragistics win grid, and after I bind, it supports clicking on column headers to resort, but I don't write any code for that or set any properties.

Nick Parker wrote Anonymous Classes Would Be Nice
on Fri, Dec 29 2006 4:54 PM

I was working on a personal project today when I needed to sort some data being returned via ActiveRecord

KK wrote re: Sorting in .Net (don't sort manually, let .Net do it for you) - level 100
on Wed, Jan 30 2008 3:26 PM

Requirement: Need to implement a 2 way sort, ie, on name, then on size

I have a user control page which has a DataBind method.In that method, I have following code....

_listAvailableAds.Sort(delegate(Crat oCrat1, Crat oCrat2) { return oCrat1.Name.CompareTo(oCrat2.Name); });

where _listAvailableAds is a generic list.

This sorts by name. I need a secondary sorting by size as well on the above, something like oCrat1.Size.CompareTo(oCrat2.Size). How to achive this in C#.

The above code is present on the DataBind method of my user control page.