xyz
xyz

Reputation: 27827

Conditional "orderby" sort order in LINQ

In LINQ, is it possible to have conditional orderby sort order (ascending vs. descending).

Something like this (not valid code):

bool flag;

(from w in widgets
 where w.Name.Contains("xyz")
 orderby w.Id (flag ? ascending : descending)
 select w)

Upvotes: 33

Views: 66161

Answers (9)

breggles
breggles

Reputation: 21

Can also roll up Richard's answer into a convenient helper method, like so:

internal static class LinqExtensions
{
    public static IOrderedEnumerable<TSource> OrderBy<TSource,TKey>
        (this IEnumerable<TSource> source,
         Func<TSource, TKey> keySelector,
         bool ascending)
    {
        return ascending ? source.OrderBy(keySelector)
                         : source.OrderByDescending(keySelector);
    }

    public static IOrderedEnumerable<TSource> ThenBy<TSource,TKey>
        (this IOrderedEnumerable<TSource> source,
         Func<TSource, TKey> keySelector,
         bool ascending)
    {
        return ascending ? source.ThenBy(keySelector)
                         : source.ThenByDescending(keySelector);
    }
}

Upvotes: 2

watbywbarif
watbywbarif

Reputation: 6977

You can even do more complex ordering and still keep it short:

    var dict = new Dictionary<int, string>() { [1] = "z", [3] = "b", [2] = "c" };
    var condition =  true;
    var result = (condition ? dict.OrderBy(x => x.Key) : dict.OrderByDescending(x => x.Value))
        .Select(x => x.Value);

Upvotes: 0

Jezze
Jezze

Reputation: 314

Here is a more general solution, that can be used for various conditional lambda expressions without breaking the flow of the expression.

public static IEnumerable<T> IfThenElse<T>(
    this IEnumerable<T> elements,
    Func<bool> condition,
    Func<IEnumerable<T>, IEnumerable<T>> thenPath,
    Func<IEnumerable<T>, IEnumerable<T>> elsePath)
{
    return condition()
        ? thenPath(elements)
        : elsePath(elements);
}

e.g.

var result = widgets
    .Where(w => w.Name.Contains("xyz"))
    .IfThenElse(
        () => flag,
        e => e.OrderBy(w => w.Id),
        e => e.OrderByDescending(w => w.Id));

Upvotes: 16

Julian Lettner
Julian Lettner

Reputation: 3659

The MoreLINQ NuGet package also provides extension methods to make this more convenient. It also provides many more helpful extension methods and is therefore a stable go-to in my projects.

Upvotes: 1

Julian Lettner
Julian Lettner

Reputation: 3659

If the ordering property Id is a number (or supports the unary minus) one could also do:

bool ascending = ...

collection.Where(x => ...)
  .OrderBy(x => ascending ? x.Id : -x.Id)
  .Select(x => ...)

// LINQ query
from x in ...
orderby (ascending ? x.Id : -x.Id)
select ...

Upvotes: 3

Richard
Richard

Reputation: 108975

If you build the expression incrementally you can do this. Generally easier using expressions rather than comprehension expressions:

var x = widgets.Where(w => w.Name.Contains("xyz"));
if (flag) {
  x = x.OrderBy(w => w.property);
} else {
  x = x.OrderByDescending(w => w.property);
}

(Assuming the Widget's property is basis of sort since you don't list one.)

Upvotes: 41

Carter Medlin
Carter Medlin

Reputation: 12465

...Or do it all in one statement

bool flag;

var result = from w in widgets where w.Name.Contains("xyz")
  orderby
    flag ? w.Id : 0,
    flag ? 0 : w.Id descending
  select w;

Upvotes: 19

cloggins
cloggins

Reputation: 669

You could try something like the following:

var q = from i in list
         where i.Name = "name"
         select i;
if(foo)
     q = q.OrderBy(o=>o.Name);
else
     q = q.OrderByDescending(o=>o.Name);

Upvotes: 8

Konamiman
Konamiman

Reputation: 50273

You can define a base query without the ordering, then order according to the flag:

var query=(from w in widgets
  where w.Name.Contains("xyz")
  select w);

var result = flag ?
  query.OrderBy(w =>w) :
  query.OrderByDescending(w = w);

Upvotes: 9

Related Questions