Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Sort collection class using LINQ for NUnit projects

4.81/5 (17 votes)
15 Mar 2010CPOL6 min read 1   186  
Provides a static class that can be called in NUnit projects for sorting collections and comparing them.

Introduction

This is a static class that can be used to sort a custom collection of objects based on a specific attribute of an object. This is useful especially when two collections of the same type with the same number of objects are loaded differently and need to be compared. For example, on a NUnit project, we need to Assert.AreEqual two collections, one populated from the database and the other populated based from an XML file with values that are expected. At this moment in time, we don't know in which order the objects are added to the collection, but the same number and same objects are part of both collections. When we are trying to compare them, the test fails because the objects are not in the same order. Using the SortCollection class, we can eliminate this problem by sorting both collections in the same way.

Prerequisites

To run the demo project supplied with this article, you will need to have Visual Studio 2008, Framework 3.5, and NUnit (2.5 or later) installed.

The Code

I have included in this article a demo project to see how the SortCollection class can be used. I will discuss the demo project later in the article. First, I would like to describe a little bit about the fields, methods, and helper methods of this class.

In the Fields region, we find a private enumeration which is used internally by the MinMaxItem private helper method item to determine the type of sorting: ascending or descending.

The Fields region contains a private enumeration:

C#
#region Fields
//===============================================
        private enum MinMax { Min, Max };
//===============================================
#endregion

Let's carry on with the helper methods region, and talk about the methods and what they achieve. The first method to expose is the private Sort method. The retuning type is an IEnumerable<TSource>. TSource can be any type of object; in my demo, I used an object called Deal. The Sort method uses Generics. The first generic type TSource is the type of object in our collection, and the second generic type TValue can by any value type that we want to use for sorting the collection. TValue is basically a property in the object. In my example, I used the Amount property which is a double, but also can be DealID which is an integer type. The Sort method has three arguments: first is our collection to sort, the second is the delegate to a function used internally by LINQ in the sorting process, and the third one is the comparer of type TValue, which again is a value type. The body method uses the LINQ method OrderBy to sort the collection based on the selector and comparer. The returning type is a LINQ sorted collection which will be converted later on in our DealCollection type using the ConvertToCollection method.

The second method in this region is MinMaxItem, which returns an object of type TSource. One difference between the standard LINQ Max method and the MinMaxItem method is, the Max method returns the maximum value in the collection, whereas my method returns the object which contains the maximum value. For example, if we have a look at the demo project, I want to find the maximum Amount in my collection, and if I use the standard Max method, the value returned is 15000, but if I use my method, then the object which has the Amount property equal to 15000 is returned. This is very useful when, for example, in our NUnit tests, one test is modifying the amount and/or other properties by processing the deal. Later in another NUnit test, we compare the deal amount and/or other properties against the test deal object properties, where we know in advance what the amount value or other property values should be. MinMaxitem has an extra argument compared with the previously described method, which is the MinMax enumeration type.

C#
#region Helpers // ======================================================
private static IEnumerable<TSource> Sort<TSource, TValue>(
    this IEnumerable<TSource> source,
    Func<TSource, TValue> selector,
    IComparer<TValue> comparer)
{
    try
    {
        IEnumerable<TSource> newSource = 
         source.OrderBy<TSource, TValue>(selector, comparer);
        return newSource;
    }
    catch (System.Exception exception)
     { throw new FormatException(exception.ToString()); }
}

//===============================================================
private static TSource MinMaxItem<TSource, TValue>(
    this IEnumerable<TSource> source,
    Func<TSource, TValue> selector,
    IComparer<TValue> comparer,
    MinMax element)
{
    try
    {
        TSource minMaxItem = default(TSource);
        TValue minMaxValue = default(TValue);

        using (var enumerator = source.GetEnumerator())
        {
            if (enumerator.MoveNext())
            {
                minMaxItem = enumerator.Current;
                minMaxValue = selector(minMaxItem);
                while (enumerator.MoveNext())
                {
                    TValue value = selector(enumerator.Current);
                    if (element == MinMax.Max ? 
                        comparer.Compare(value, minMaxValue) > 0 : 
                        comparer.Compare(value, minMaxValue) < 0)
                    {
                        minMaxItem = enumerator.Current;
                        minMaxValue = value;
                    }
                }
            }
        }
        return minMaxItem;
    }
    catch (System.Exception exception)
    { throw new FormatException(exception.ToString()); }           
}
//===============================================================
#endregion

The region Methods expose the following: MaxItem, MinItem, SortAscending, SortDescending, and ConvertToCollection. Let's talk about every method, starting with MaxItem. The MaxItem public function has a returning type of TSource, which in our case is of type Deal. This method calls the MinMaxItem private method, which returns the object having the maximum value of the property we are supplying. Same with MinItem, the difference is the method returns the object having the minimum value of the property we are supplying. SortAscending and SortDescending are pretty similar; the latter uses the Reverse method to reverse the order of the collection. The last method to discuss here is the ConverToCollection method which is public, so it can be used in other situations. This method converts an IEnumarable collection into a custom collection.

C#
#region Methods
//=================================================================
public static TSource MaxItem<TSource, TValue>(this 
       IEnumerable<TSource> source, Func<TSource, TValue> selector)
{
  return MinMaxItem<TSource, TValue>(source, selector, 
       Comparer<TValue>.Default, MinMax.Max);
}
//=================================================================
public static TSource MinItem<TSource, TValue>(this 
       IEnumerable<TSource> source, Func<TSource, TValue> selector)
{
  return MinMaxItem<TSource, TValue>(source, selector, 
         Comparer<TValue>.Default, MinMax.Min);
}
//=================================================================
public static C SortAscending<TSource, TValue, C>(this 
       IEnumerable<TSource> source, Func<TSource, TValue> selector)
where C : IList<TSource>, new()
{
return ConvertToCollection<C, TSource>(Sort<TSource, 
       TValue>(source, selector, Comparer<TValue>.Default));
}
//=================================================================
public static C SortDescending<TSource, TValue, C>(this 
       IEnumerable<TSource> source, Func<TSource, TValue> selector)
where C : IList<TSource>, new()
{
return ConvertToCollection<C, TSource>(Sort<TSource, TValue>(source, 
       selector, Comparer<TValue>.Default).Reverse());
}
//=================================================================
public static C ConvertToCollection<C, T>(
                this IEnumerable linqCollection)
where C : IList<T>, new()
{
C collection = new C();
//
foreach (var item in linqCollection)
{
collection.Add((T)item);
}
return collection;
}
//=================================================================
#endregion

Using the code

First, we need to look at the demo project. Let's have a look at the SortedCollectionTest NUnit method and how the SortAscending method is called. As we can see, SortAscending has three generic types that we need to supply when we call it. The first generic type is the type of the object; in our case, this type is Deal. The next generic type is the value type that we'll use to sort the collection; in our case, double. I use double here because I want to sort the collection based on the Amount property of the Deal object, which is a double. We also can use, for example, int if we are going to sort the collection after DealID. The third generic is the DealCollection type, and it will be used in the conversion of the IEnumerable type into the DealCollection type after sorting. The method takes one argument: the selector (deal => deal.Amount) which is used to specify the property and its type to sort the collection.

Call the SortAscending method like this:

C#
sortedCollA = dealCollectionA.SortAscending<Deal, 
                    double, DealCollection>(deal => deal.Amount);
sortedCollB = dealCollectionB.SortAscending<Deal, 
                    double, DealCollection>(deal => deal.Amount);

Conclusion

In conclusion, the developer can use existing LINQ methods like OrderBy, OrderDescendingBy, and Cast etc., to achieve the same result. In my opinion, using this static class can have some potential advantages like:

  • Using the same class methods for all the projects provides consistency
  • No need for including the using Linq directive in projects to be able to sort a collection or extract Min or Max, keeping intellisense cleaner
  • SortAscending or SortDescending automatically converts an IEnumerator type collection into a specified type collection, and returns the new type of collection

Points of Interest

The main point of interest here is that sometimes we have no control of how collections that need to be compared are populated, and we need to be able to sort them in a way. This is just another way of sorting a collection.

What Do You Think?

If you liked the article, please vote for it. If you have any questions, please don't hesitate to ask, or if you think that something can be done in a better way, please say it. Thanks for your time, and I hope this article brings something useful to developers.

History

  • V1.0: 6/11/2009

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)