I recently came upon a situation where I had a list of objects each of which had properties. These objects were pulled from Active Directory, with various properties of the AD objects loaded in a
StringDictionary. I wanted to be able to sort these objects based on configuration, without hardcoding any particular property or order to sort on, and I wanted to be able to sort on multiple properties, so for example,
LastName Ascending, then
Of course, you can't simply iterate through the list of properties that you want to sort on and sort, because each sort would wipe out the previous sort.
LINQ does provide methods to handle this scenario,
ThenBy. But without knowing the properties that you want to sort by at compile time, it's just a tiny bit trickier. In this article, I demonstrate a simple generic function that helps to sort lists of data on an arbitrary number of sort keys, with each level of sort supporting ascending and descending order.
Here is the helper function:
private IEnumerable<T> MultiLevelSort<T, SK>(
Func<T, SK, string> keySelector,
Func<SK, bool> ascendingSelector)
if (sortKeys.Count == 0) return list;
IOrderedEnumerable<T> res = null;
for (int i = 0; i < sortKeys.Count; i++)
SK sk = sortKeys[i];
bool ascending = ascendingSelector(sk);
if (i == 0)
if (ascending) res = list.OrderBy(r => keySelector(r, sk));
else res = list.OrderByDescending(r => keySelector(r, sk));
if (ascending) res = res.ThenBy(r => keySelector(r, sk));
else res = res.ThenByDescending(r => keySelector(r, sk));
This code takes in four parameters:
IEnumerable<T> of objects to sort
List<SK> of objects that contain sorting order information (note that this list itself is expected to already be in the correct sort order)
Func<T, SK, string> to extract the value from
T based on information in SK to actually sort on
Func<SK, bool> to extract the ascending/descending information from SK -
true means ascending,
false is descending
… and it returns the list correctly sorted as an
IEnumerable<T>. Note that the actual object returned is an
IOrderedEnumerable<T> as long as there is at least one valid sort key.
Using the Code
Here is an example of how to use the helper function. It assumes that you have a list of '
MyProperty' in an object called '
AllProperties', of which some specify sorting information.
List<MyProperty> sortProps = AllProperties
.Where(sp => sp.Sort != string.Empty)
.OrderBy(sp => sp.SortOrder).ToList();
IEnumerable<MyObject> sortedResults = MultiLevelSort<MyObject, MyProperty>(
(r, sp) => r.Properties.ContainsKey(sp.Name) ? r.Properties[sp.Name] : string.Empty,
sp => sp.Sort == "Ascending"
... where (in this example)
MyObject is an object that contains a
StringDictionary called ‘
MyProperty is an object that contains properties called ‘
Sort’ (“Ascending/Descending”), ‘
SortOrder’ (an integer), and ‘
Name’ (the name of the property within the
MyObject.Properties collection that we want to sort on).
Your objects can obviously have their own structures or properties that provide the data to sort on - it doesn't have to be
StringDictionary as in this example.
- v1.0 - 2009 Oct 26 - Initial publication