Mathias Brandewinder on .NET, F#, VSTO and Excel development, and quantitative analysis / machine learning.
by Mathias 26. December 2010 11:38

A little while ago, I looked into dynamically sorting a list, based on a criterion supplied by the user. There was one limit on the solution I presented, however: only one criterion could be applied at a time. What if we wanted to sort by multiple criteria?

For instance, using the same example as in the previous post, if we had a list of Products with a Name, a Supplier and a Price, how would we go about sorting by Supplier and then by Price? Or by Name and then Supplier? And how could we make that flexible, so that we can sort by an arbitrary number of criteria. This would be particularly useful if we had in mind the development of a user interface where users could select how they want to see a list of items displayed on screen.

Besides OrderBy, LINQ provides the ThenBy extension method. ThenBy is applied to an IOrderedEnumerable – a collection that has already been ordered – and sorts its, maintaining the existing order. For instance, in the following example, products are first sorted by Supplier, and for each Supplier, by Price:

private static void Main(string[] args)
{
   var apple = new Product() { Supplier = "Joe's Fruits", Name = "Apple", Price = 1.5 };
   var apricot = new Product() { Supplier = "Jack & Co", Name = "Apricot", Price = 2.5 };
   var banana = new Product() { Supplier = "Joe's Fruits", Name = "Banana", Price = 1.2 };
   var peach = new Product() { Supplier = "Jack & Co", Name = "Peach", Price = 1.5 };
   var pear = new Product() { Supplier = "Joe's Fruits", Name = "Pear", Price = 2 };

   var originalFruits = new List<Product>() { apple, apricot, banana, peach, pear }; 

   Func<Product, IComparable> sortByPrice = (product) => product.Price;
   Func<Product, IComparable> sortByName = (product) => product.Name;
   Func<Product, IComparable> sortBySupplier = (product) => product.Supplier;

   var sortedFruits = originalFruits
      .OrderBy(sortBySupplier)
      .ThenBy(sortByPrice);

   foreach (var fruit in sortedFruits)
   {
      Console.WriteLine(string.Format("{0} from {1} has a price of {2}.",
         fruit.Name,
         fruit.Supplier,
         fruit.Price));
   }

   Console.ReadLine();
}

More...

by Mathias 31. October 2010 12:36

Last week, an interesting problem came on my desk. Initially, when I was asked to sort items, I didn’t think much of it. Given a list of items, it’s fairly trivial to use LINQ and sort it by whatever property you want. What I hadn’t quite anticipated was that the user should be able to select between multiple sorting criteria.

If there was a predetermined sorting criterion, the problem would be straightforward. For instance, given a list of Fruits with a name, supplier and price, I can easily sort them by price:

static void Main(string[] args)
{
   var apple = new Product() { Supplier = "Joe's Fruits", Name = "Apple", Price = 1.5 };
   var apricot = new Product() { Supplier = "Jack & Co", Name = "Apricot", Price = 2.5 };
   var banana = new Product() { Supplier = "Joe's Fruits", Name = "Banana", Price = 1.2 };
   var peach = new Product() { Supplier = "Jack & Co", Name = "Peach", Price = 1.5 };
   var pear = new Product() { Supplier = "Joe's Fruits", Name = "Pear", Price = 2 };

   var originalFruits = new List<Product>() { apple, apricot, banana, peach, pear };

   var sortedFruits = originalFruits
      .OrderBy(fruit => fruit.Price);

   foreach (var fruit in sortedFruits)
   {
      Console.WriteLine(string.Format("{0} from {1} has a price of {2}.", 
         fruit.Name, 
         fruit.Supplier, 
         fruit.Price));
   }

   Console.ReadLine();
}

Running this simple console application produces the following list, nicely sorted by price:

BasicSorting

However, if we want to give the user to select how fruits should be sorted, the problem becomes a bit more complicated. We could write a switch statement, with something like “if 1 is selected, then run this sort, else run that sort, else run that other sort”, and so on. It would work, but it would also be ugly. We would be  re-writing essentially the same OrderBy statement over and over again, something which reeks of code duplication. How could we avoid that, and keep our code smelling nice and fresh?

More...

Comments

Comment RSS