Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic Linq ordering function?

Tags:

c#

lambda

linq

I would like to be able to pass in a Func<T, ?> that allows me to choose exactly how to sort a list of items... the issue I have is the the return type might vary... so for example I want to do something like this (not production code):

Func<POline, string> poLineOrder
if (option) poLineOrder = poline => poline.PartNumber;
else poLineOrder = poline => poline.LineOrder;
var orderedLines = poLines.OrderBy(poLineOrder);

The issue here is that while PartNumber is a string, LineOrder is an int, and this code doesn't compile. I have a feeling I'm going to have to build an Expression (?) but I can't seem to get the syntax right.

(Yes, I can solve this, by using poline.LineOrder.ToString("D10") but I'm asking the more general question here... thanks!)

-mdb

like image 324
Michael Bray Avatar asked Jun 08 '09 18:06

Michael Bray


People also ask

How to order by ascending in LINQ?

In LINQ, the OrderBy operator is used to sort the list/ collection values in ascending order. In LINQ, if we use order by the operator by default, it will sort the list of values in ascending order. We don't need to add any ascending condition in the query statement.

Is LINQ OrderBy stable?

This method performs a stable sort; that is, if the keys of two elements are equal, the order of the elements is preserved.

What is lambda expression in LINQ?

The term 'Lambda expression' has derived its name from 'lambda' calculus which in turn is a mathematical notation applied for defining functions. Lambda expressions as a LINQ equation's executable part translate logic in a way at run time so it can pass on to the data source conveniently.


2 Answers

OrderBy expects a Key-Selector-function of type Func<Item, Key> and an optional IComparer<Key> to define the comparison itself.

You'll need the following helper definitions:

 class FunctorComparer<T> : IComparer<T> {
        private readonly Comparison<T> m_Comparison;

        public FunctorComparer(Comparison<T> Comparison) {
            this.m_Comparison = Comparison;
        }

        public int Compare(T x, T y) {
            return m_Comparison(x, y);
        }
    }

    static Comparison<TKey> Compare<TKey, TRes>(Func<TKey, TRes> Selector) where TRes : IComparable<TRes> {
        return (x, y) => Selector(x).CompareTo(Selector(y));
    }

Now you can write

Comparison<POLine> x;

if (option) 
   x = Compare<POLine, int>(l => l.PartNumber);
else
   x = Compare<POLine, string>(l => l.LineOrder);

var res = poLines.OrderBy(i => i, new FunctorComparer<POLine>(x));
like image 63
Dario Avatar answered Sep 23 '22 23:09

Dario


You might want to consider using Dynamic LINQ from the VS2008 Samples. Then you can do:

var orderedLines = poLines.OrderBy( poLineOrder ? "PartNumber" : "LineOrder" );
like image 39
tvanfosson Avatar answered Sep 21 '22 23:09

tvanfosson