Adsense

Tuesday, May 27, 2008

How to Sort a generic IList or List

A while back I wrote a post on How to sort a Generic IList. Here is another approach.
To sort a generic List<T>, you can create a class that implements IComparer<T> and tells the list how to sort itself. Its fairly straight forward and the example below shows how to sort Widgets.
A Widget is a simple class that has a public string Name and a public int Size. I want to be able to sort generic lists of widgets (List<Widget>) by name and by size. You can see my code below.
The widget and sorter implementations
public class Widget {
    public string Name = string.Empty;
    public int Size = 0;

    public Widget(string name, int size) {
this.Name = name;
this.Size = size;
}
}

public class WidgetNameSorter : IComparer<Widget> {
    public int Compare(Widget x, Widget y) {
        return x.Name.CompareTo(y.Name);
}
}

public class WidgetSizeSorter : IComparer<Widget> {
    public int Compare(Widget x, Widget y) {
return x.Size.CompareTo(y.Size);
}
}
And application to create the list and sort it
class Program {
static void Main(string[] args) {
List<Widget> widgets = new List<Widget>();
widgets.Add(new Widget("Zeta", 6));
widgets.Add(new Widget("Beta", 3));
widgets.Add(new Widget("Alpha", 9));

OutputWidgets(widgets);
widgets.Sort(new WidgetNameSorter());
OutputWidgets(widgets);
widgets.Sort(new WidgetSizeSorter());
OutputWidgets(widgets);

Console.ReadLine();
}

public static void OutputWidgets(List<Widget> widgets) {
foreach (Widget w in widgets) {
Console.WriteLine(string.Format("{0} - {1}", w.Name, w.Size));
}
Console.WriteLine(string.Empty);
}
}
Running the application above results in the following output.
output
Straight forward enough? =)
So what do you do if you have a generic IList<T> and want to sort it? Since you only have the interface reference that can't make assumptions about what's implementing it, you have no Sort method. You can use the ToList() method to convert the IList<T> into a List<T>, and then call the Sort() method. See the updated example below. (Note: I previously blogged about a way to sort a generic IList using the ArrayList.Adapter. You can read about it HERE.)
class Program {
static void Main(string[] args) {
IList<Widget> widgets = new List<Widget>();

widgets.Add(new Widget("Zeta", 6));
widgets.Add(new Widget("Beta", 3));
widgets.Add(new Widget("Alpha", 9));

OutputWidgets(widgets);
widgets.ToList().Sort(new WidgetNameSorter());
OutputWidgets(widgets);
widgets.ToList().Sort(new WidgetSizeSorter());
OutputWidgets(widgets);

Console.ReadLine();
}

public static void OutputWidgets(IList<Widget> widgets) {
OutputWidgets(widgets.ToList());
}

public static void OutputWidgets(List<Widget> widgets) {
foreach (Widget w in widgets) {
Console.WriteLine(string.Format("{0} - {1}", w.Name, w.Size));
}
Console.WriteLine(string.Empty);
}
}

2 comments:

Unknown said...

Hi!

I like more this one... because I need to create a few compares for more than one usase.....


Thanks...

DaddyCool said...

I'm using VS2008 targetted to .NET Framework 3.5. Using the Comparer classes this example didn't worked out for me. The IList did not get sorted. Therefor I found a workaround using Linq for Objects.

The main program becomes:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IListSorter
{
class Program
{
static void Main(string[] args)
{
IList<Widget> widgets = new List<Widget>();
widgets.Add(new Widget("Zeta", 6));
widgets.Add(new Widget("Beta", 3));
widgets.Add(new Widget("Alpha", 9));

OutputWidgets(widgets);

var query = from w in widgets orderby w.Name select w;
widgets = query.ToList<Widget>() as IList<Widget>;
OutputWidgets(widgets);

query = from w in widgets orderby w.Size select w;
widgets = query.ToList<Widget>() as IList<Widget>;
OutputWidgets(widgets);
Console.ReadLine();
}

public static void OutputWidgets(IList<Widget> widgets)
{
OutputWidgets(widgets.ToList());
}

public static void OutputWidgets(List<Widget> widgets)
{
foreach (Widget w in widgets)
{
Console.WriteLine(string.Format("{0} - {1}", w.Name, w.Size));
}
Console.WriteLine(string.Empty);
}
}
}

the main difference is that the result of the query is converted first to List<Widget> and afterwards 'converted' to IList&ltWidget&gt with the 'as' statement.

The IComparer classes are not needed anymore. In the query you can specify on what element(s) need to be sorted.