Click here to Skip to main content
15,868,141 members
Articles / Programming Languages / C#
Article

ObjectComparer

Rate me:
Please Sign up or sign in to vote.
4.89/5 (56 votes)
10 Feb 20032 min read 137.3K   330   66   22
Sorting arrays of objects on arbitrary fields

Sample Image - shot.jpg

Introduction

Sooner or later, you will have an array of objects you'd like to sort. Unlike simple types like strings or integers, your objects usually have several properties you or your user want to sort on.

This article explains how to write a simple class which works with Array.Sort() to sort arrays of objects on dinamically selected fields. It's very similar to what DataView.Sort does for DataTables.

Background

Back in the old C times, when you wanted to use the standard sort or search functions you had to write a function to compare two objects of the desired type. The function received pointers to the objects, and returned -1, 0 or 1 when the first value was smaller, equal or bigger than the second.

You will then pass your function to the C sort function, using a function pointer (something similar to .NET's delegates).

This dirty but efficient technique allowed to create a single array sort function, that worked with any type of objects you wrote a comparer for.

Thinking in objects

In .NET, the approach to this problem is very similar. The Array.Sort method uses an IComparer with a method called Compare, that receives two objects and returns exactly the same as our old C comparer.

The class

The criteria to sort an array of objects is usually formed by the name of the fields, and an indication of ascending or descending order (like an SQL "ORDER BY" clause).

So, the constructor of our class accepts exactly those parameters:

C#
public ObjectComparer(string[] fields, bool[] descending)
{
    Fields = fields;
    Descending = descending;
}

To make it easier to use, I also implemented a constructor for "all ascending" fields:

C#
public ObjectComparer(params string[] fields) : this(fields, new bool[fields.Length]) {}

The alternate constructor just calls the first one with an array of false booleans for the descending parameter. Remember that, since bools are structs, they are initialized to 0 (false) on array creation.

Now, the method that does all the work

C#
public int Compare(object x, object y)
{
    //Get types of the objects
    Type typex = x.GetType();
    Type typey = y.GetType();

    for(int i = 0; i<Fields.Length; i++)
    {
        //Get each property by name
        PropertyInfo pix = typex.GetProperty(Fields[i]);
        PropertyInfo piy = typey.GetProperty(Fields[i]);

        //Get the value of the property for each object
        IComparable pvalx = (IComparable)pix.GetValue(x, null);
        object pvaly = piy.GetValue(y, null);

        //Compare values, using IComparable interface of the property's type
        int iResult = pvalx.CompareTo(pvaly);
        if (iResult != 0)
        {
            //Return if not equal
            if (Descending[i])
            {
                //Invert order
                return -iResult;
            }
            else
            {
                return iResult;
            }
        }
    }
    //Objects have the same sort order
    return 0;
}

Note that I don't implement ANY error-checking code. It's such a simple class that it's not worth it. Be sure to call it with correct parameters (existing properties, and two same-size parameter arrays).

Comparing apples and bananas?

The key here, is that the two objects don't have to be the same type, as long as they implement the same property, and the property is a type that implements IComparable. So, you could sort Controls based on their TabIndex, since it's an Int32.

Using the code

First build the ObjectComparer library, and then open the PersonSort project.

This sample project implements a simple class, Person, which represents a person with a Name (string) and an Age (int). We first create an array of persons:

C#
Person[] personArray = new Person[]
{
    new Person("Joey", 21),
    new Person("Johnny", 30),
    new Person("Marky", 28),
    new Person("C.J.", 28),
    new Person("Joey", 25),
    new Person("Dee Dee", 33)
};

And then we can sort in any field we want:

C#
//Sort by Age
Array.Sort(personArray, new ObjectComparer("Age"));
//Sort by Name, Age
Array.Sort(personArray, new ObjectComparer("Name", "Age"));
//Sort by Name, Age DESC
Array.Sort(personArray, new ObjectComparer(
    new string[]{"Name","Age"},
    new bool[]{false,true}
    ));

Conclusion

ObjectComparer is a nice tool for sorting arrays of objects on programmer or user request.

Whenever you create this kind of utility class, it's important to create good constructors, so they can be created and used in just one line.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
NewsExtension for generics Pin
Marcus Deecke28-Dec-05 0:07
Marcus Deecke28-Dec-05 0:07 
Great article.
I added the inheritance declaration of System.Collections.Generic.IComparer<object>.
Now it's also working for generic lists of objects:
<br />
List<object> = new List<object>();<br />
<br />
list.Add(new Person("Joey", 21));<br />
...<br />
list.Add(new Person("Dee Dee", 33));<br />
<br />
list.Sort(new ObjectComparer(new string[] { "Name" }));<br />

GeneralQuestion for ANYONE Pin
kopykake4-Dec-05 11:16
kopykake4-Dec-05 11:16 
GeneralRe: Question for ANYONE Pin
kopykake4-Dec-05 11:24
kopykake4-Dec-05 11:24 
GeneralGreat Article Pin
miah alom9-Nov-05 11:24
miah alom9-Nov-05 11:24 
GeneralMulti-Property, Multi-Directional Pin
coar16-Aug-05 13:29
coar16-Aug-05 13:29 
GeneralGreat one; check for nulls Pin
Anonymous26-Oct-04 10:15
Anonymous26-Oct-04 10:15 
GeneralRe: CORRECTION: check for nulls Pin
cpy26-Oct-04 11:49
cpy26-Oct-04 11:49 
GeneralVersion for both Fields &amp; Properties Pin
Oskar Austegard22-Mar-04 12:18
Oskar Austegard22-Mar-04 12:18 
GeneralOutstanding Pin
Simon Segal9-Dec-03 18:19
Simon Segal9-Dec-03 18:19 
GeneralWorks like a charm... Pin
jonx1-Dec-03 15:08
jonx1-Dec-03 15:08 
QuestionWhat about the perf? Pin
Quentin Pouplard16-Apr-03 3:57
Quentin Pouplard16-Apr-03 3:57 
AnswerRe: What about the perf? Pin
Diego Mijelshon16-Apr-03 4:35
Diego Mijelshon16-Apr-03 4:35 
GeneralMisnomer Pin
Simon Trew18-Feb-03 22:54
sussSimon Trew18-Feb-03 22:54 
GeneralRe: Misnomer Pin
Diego Mijelshon24-Feb-03 1:27
Diego Mijelshon24-Feb-03 1:27 
GeneralRe: Mordejai Pin
azusakt16-Jun-03 18:22
azusakt16-Jun-03 18:22 
GeneralRe: Mordejai Pin
Diego Mijelshon17-Jun-03 7:24
Diego Mijelshon17-Jun-03 7:24 
GeneralExcellent Pin
Marc Clifton12-Feb-03 1:34
mvaMarc Clifton12-Feb-03 1:34 
GeneralRe: Excellent Pin
Diego Mijelshon12-Feb-03 1:44
Diego Mijelshon12-Feb-03 1:44 
GeneralRe: Excellent Pin
Marc Clifton12-Feb-03 1:49
mvaMarc Clifton12-Feb-03 1:49 
GeneralRe: Excellent Pin
Noah Duke12-Feb-03 5:04
Noah Duke12-Feb-03 5:04 
GeneralRe: Excellent Pin
Diego Mijelshon12-Feb-03 6:19
Diego Mijelshon12-Feb-03 6:19 
GeneralGreat Pin
Steve McLenithan11-Feb-03 11:14
Steve McLenithan11-Feb-03 11:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.