Ordering a .NET data sequence with various LINQ operators
August 8, 2017 Leave a comment
A sequence of any object can be easily ordered with the following LINQ operators in .NET:
- OrderBy
- ThenBy
- OrderByDescending
- ThenByDescending
Consider the following sequence:
string[] bands = { "ACDC", "Queen", "Aerosmith", "Iron Maiden", "Megadeth", "Metallica", "Cream", "Oasis", "Abba", "Blur", "Chic", "Eurythmics", "Genesis", "INXS", "Midnight Oil", "Kent", "Madness", "Manic Street Preachers", "Noir Desir", "The Offspring", "Pink Floyd", "Rammstein", "Red Hot Chili Peppers", "Tears for Fears" , "Deep Purple", "KISS"};
The first overload of OrderBy lets you specify the property by which to order the elements and they will be sorted by a default comparer. This is how to order the list of bands by the string length:
IEnumerable<string> ordered = bands.OrderBy(band => band.Length); foreach (string item in ordered) { Console.WriteLine(item); }
The second overload of OrderBy lets you specify a comparer which will determine the order of elements. The comparer must implement the IComparer of T interface. Say you’d like to order the bands by the number of consonants in their names. The following comparer would do:
public class ConsonantNumberComparer : IComparer<string> { public int Compare(string x, string y) { int consonantCountStringOne = GetConsonantCount(x); int consonantCountStringTwo = GetConsonantCount(y); if (consonantCountStringOne < consonantCountStringTwo) return -1; if (consonantCountStringOne > consonantCountStringTwo) return 1; return 0; } private int GetConsonantCount(string input) { char[] consonants = new char[] { 'b', 'c', 'd', 'f', 'g', 'h','k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'z', 'x', 'y' }; int count = 0; foreach (char c in input.ToLower()) { if (consonants.Contains(c)) { count++; } } return count; } }
You can use this comparer as follows:
IEnumerable<string> ordered = bands.OrderBy(band => band, new ConsonantNumberComparer()); foreach (string item in ordered) { Console.WriteLine(item); }
You can specify additional ordering rules using the ThenBy operator. The following query will order the items by the comparer we’ve just constructed and then in alphabetical order:
IEnumerable<string> ordered = bands.OrderBy(band => band, new ConsonantNumberComparer()).ThenBy(band => band); foreach (string item in ordered) { Console.WriteLine(item); }
Don’t use the OrderBy() operator multiple times like this in order to fulfil what ThenBy is doing:
IEnumerable<string> ordered = bands.OrderBy(band => band, new ConsonantNumberComparer()).OrderBy(band => band);
This will first order the items according to the ConsonantNumberComparer and then completely re-order the items in alphabetical order. Therefore the first order by is cancelled out in the final sequence.
The OrderByDescending and ThenByDescending operators do exactly what OrderBy and ThenBy but in reverse order as the names suggest:
IEnumerable<string> ordered = bands.OrderByDescending(band => band, new ConsonantNumberComparer()).ThenByDescending(band => band); foreach (string item in ordered) { Console.WriteLine(item); }
ThenBy, OrderByDescending and ThenByDescending can also accept an IComparer object just like OrderBy.
View the list of posts on LINQ here.