Bill Blogs in C#

Bill Wagner discusses C#, LINQ, and other items of interest

Ordering LINQ results
OrderBy, ThenBy, and Descending. Oh my

The last installment discussed how some of the extension methods delivered with the preview extract portions of a collection (either the first elements, or the last elements. That’s cool, but it’s rather pointless without the ability to order the elements based on some criteria you choose. So, the next samples in the preview discuss ordering operators.
First, a parenthetical side note. In the first few blog entries where I discussed LINQ, I was very deliberate about how I went over the code. I dissected every line of code. I explained every language feature. The next few entries were a bit less detailed. I still covered every sample in full, but I assumed a bit more background (both for me, and the reader). Well, now we reach the next level. If you’ve been following along, you’ve seen enough code that you should be familiar with the basic LINQ syntax. If not, see the links at the bottom of the page. 
LINQ provides the orderby contextual keyword to order a collection:

string[] words = { "cherry", "apple", "blueberry" };
var sortedWords =
    from w in words
        orderby w
        select w;

The collection sortedWords will appear in alphabetical order. The default ordering for strings is based on alphabetical order. Pretty simple, right? 
But, of course, you’ll often want to order a collection using other properties of the collection. And, LINQ supports a number of ways to do that.  First, you can specify a different property of the collection elements as the ordering field:

string[] words = { "cherry", "apple", "blueberry" };
var sortedWords =
    from w in words
        orderby w.Length
        select w;

Or:

List<Product> products = GetProductList();
var sortedProducts =
    from p in products
    orderby p.ProductName
    select p;

You’ve seen that you can order a collection using the default order for any of the fields in the objects that make up the collection. But, of course, this isn’t the only ordering you’ll need. Suppose you need to order a set of strings without concern for case. You can write a comparer, and use that for your ordering relation:

public class CaseInsensitiveComparer : IComparer<string>
{
    public int Compare(string x, string y)
    { return string.Compare(x, y, true); }
}
string[] words = { "aPPLE", "AbAcUs", "bRaNcH", 
"BlUeBeRrY", "ClOvEr", "cHeRry"};
var sortedWords = words.OrderBy(a => a,
new CaseInsensitiveComparer());

Note that the syntax above is a bit different. Rather than using the contextual orderby keyword, you explicitly call the OrderBy extension method.
And, clearly, descending order is important:

double[] doubles = { 1.7, 2.3, 1.9, 4.1, 2.9 };
var sortedDoubles =
    from d in doubles
    orderby d descending
    select d;

If you can order collections of builtin types using the descending keyword, you can order collections of user defined types the same way:

List<Product> products = GetProductList();
var sortedProducts =
    from p in products
    orderby p.UnitsInStock descending
    select p;

There’s nothing magic about any of the individual ordering clauses. You can combine the descending order with a custom comparer:

string[] words = { "aPPLE", "AbAcUs", "bRaNcH", 
"BlUeBeRrY", "ClOvEr", "cHeRry"};
var sortedWords = words.OrderByDescending(a => a,
new CaseInsensitiveComparer());

Of course, you’ll often want to combine orderby collections in order to create primary and secondary orderings:

string[] digits = { "zero", "one", "two", "three", "four", 
    "five", "six", "seven", "eight", "nine" };
var sortedDigits =
    from d in digits
    orderby d.Length, d
    select d;

Those multiple orderby clauses are implemented using the OrderBy and the ThenBy extension methods, as shown below:

string[] words = { "aPPLE", "AbAcUs", "bRaNcH", 
    eBeRrY", "ClOvEr", "cHeRry"};
var sortedWords =
    words.OrderBy(a => a.Length)
         .ThenBy(a => a, new CaseInsensitiveComparer());

And, combining multiple can also be combined with the descending keyword, which can be applied individually to each ordering:

List<Product> products = GetProductList();
var sortedProducts =
    from p in products
    orderby p.Category, p.UnitPrice descending
    select p;

Setting a descending order is accomplished by a different extension method, OrderByDescending, or ThenByDescending:

string[] words = { "aPPLE", "AbAcUs", "bRaNcH", 
    "BlUeBeRrY", "ClOvEr", "cHeRry"};
var sortedWords =
    words.OrderBy(a => a.Length)
    .ThenByDescending(a => a, new CaseInsensitiveComparer());

Finally, you should be aware of the Reverse extension method. Reversing a collection is more efficient than performing a full sort, so if you know you have the collection sorted, but backwards, that’s the right choice:

string[] digits = { "zero", "one", "two", "three", "four", 
    "five", "six", "seven", "eight", "nine" };
var reversedIDigits = (
    from d in digits
    where d[1] == 'i'
    select d)
    .Reverse();

In this entry, we saw that LINQ provides a set of operators to order results. Next time, we’ll cover the grouping operators.



Part 1
The general query syntax
Part 2
The one where I discuss Object and Collection initializers
Part 3
The one where I finish restriction operators
Part 4
Beginning to discuss projections
Part 5
Anonymous types and projections
Part 6
Discussing indexed, filtered, and compound queries
Part 7
Finishing up the projection items
Part 8
Projection Operators and Extension methods
Published Wednesday, March 29, 2006 7:24 PM by wwagner
Filed under: ,

Comments

No Comments