1

I would like to make a sorting extension method which will take a Generic Collection and sort it using one or more keys. The keys will be properties of the collection's containing objects.

A sample LINQ query with 3 keys looks like this.

studentResults.OrderBy(x => x.CG).ThenBy(x => x.Student.Roll)
                                    .ThenBy(x => x.Student.Name).ToList();

I have already found something which can do this with one key.

public static List<TSource> OrderByAsListOrNull<TSource, TKey>(
        this ICollection<TSource> collection, Func<TSource,TKey> keySelector)
    {

        if (collection != null && collection.Count > 0) {
            return collection
                .OrderBy(x => keySelector(x))
                .ToList();
        }
        return null;
    }

I thought of using IEnumerable<Func<TSource, TKey> keySelector>, but I cannot call the function like that.

So, how may I implement a method of this kind?

Asiful Nobel
  • 133
  • 3
  • 11
  • Possible duplicate of [Dynamic LINQ OrderBy on IEnumerable](https://stackoverflow.com/questions/41244/dynamic-linq-orderby-on-ienumerablet) – ASh Mar 11 '18 at 07:35
  • @Ash in this question, the OP seems to want to use strong typing in his `OrderBy`. In your linked Dupe, the OP wanted to build a weakly typed ordering by parsing strings representing a SQL order by syntax?. – StuartLC Mar 11 '18 at 07:39

1 Answers1

3

In theory, you could build a multi-levelled sort extension, which diffentiates between the initial OrderBy and the subsequent ThenBys for secondary, tertiary sorting tiebreakers. Since by taking multiple order functions, each of which could reference a different type, you'll need to soften the projected type (I've used object, below).

public static class Extensions
{
    public static IEnumerable<T> MyOrderBy<T>(
        this IEnumerable<T> source, 
        params Func<T, object>[] orders)
    {
        Debug.Assert(orders.Length > 0);
        var sortQuery = source.OrderBy(orders[0]);
        foreach(var order in orders.Skip(1))
        {
            sortQuery = sortQuery.ThenBy(order);
        }
        return sortQuery;
    }
}

public class Poco
{
    public string Name {get; set;}
    public int Number {get; set;}
}


void Main()
{
    var items = new []{
        new Poco{Name = "Zebra", Number = 99}, 
        new Poco{Name = "Apple", Number = 123}};

    foreach(var poco in items.MyOrderBy(i => i.Number, i => i.Name))
    {
        Console.WriteLine(poco.Name);
    }
}

The problem with this (as with your original function) is that you'll probably want to order by descending at some point. Although for numeric sort functions this could be hacked by passing a *-1, it's going to be really difficult to do this for an arbitrary type

// Hack : Order a numeric descending
item => item.Number * -1

For me, I would just stay with Linq's sorting extensions, and not try to abstract them in any way!

StuartLC
  • 96,413
  • 17
  • 181
  • 256
  • i.e. From a readability point of view, Linq's `items.OrderBy(i => i.Number).ThenBy(i => i.Name)` is much clearer than our `items.MyOrderBy(i => i.Number, i => i.Name)` – StuartLC Mar 11 '18 at 08:04