简体   繁体   中英

C# Linq OrderBy based on condition

I have a foreach loop that I want to change:

foreach (var line in lines.OrderBy(x=> x.ColA))

If a condition is met, then instead of ordering by ColA, I want to order by ColB.

I know this could be done like the following:

var orderLines = new List<OrderLines>();
if (condition)
    orderLines = lines.OrderBy(x => x.ColB).ToList();
else
    orderLines = lines.OrderBy(x => x.ColA).ToList(); ;

foreach (var line in orderLines)

But I am sure there is a more elegant solution.

Several solutions.

(1) Don't do the ToList() before your foreach, only create the IEnumerable.

IEnumerable<OrderLines> orderLines = condition ?
    lines.OrderBy(orderLine => orderLine.ColB) :
    lines.OrderBy(orderLine => orderLine.ColA);

foreach(OrderLine orderlLine in orderLines) {...}

(2) If you will be using this on several locations, consider to create an extension method. This way your method looks like any other LINQ method. See extension methods demystified

public static IEnumerable<OrderLine> OrderBy(
    this IEnumerable<OrderLine> source,
    bool condition)
{
    return condition ?
    lines.OrderBy(orderLine => orderLine.ColB) :
    lines.OrderBy(orderLine => orderLine.ColA);
}

Usage:

If operator checks chexBox1, sort by colB, else sort by colA:

IEnumerable<OrderLine> lines = ...
foreach(var sortedOrderLine in lines.OrderBy(this.CheckBox1.IsChecked))
{
    ...
}

Because it is an extension method of IEnumerable<OrderLine> , you can even intertwine it with other LINQ methods:

var result = lines.Where(orderLine => orderLine.Date.Year >= 2020)
                  .OrderBy(this.checkBox1.IsChecked)
                  .Select(orderLine => new
                  {
                       Id = orderLine.Id,
                       Price = orderLine.Price,
                  });

But all in all, it doesn't save you a lot of code. The only advantage would be if you would use it in a lot of methods. In that case, a change in how you want to OrderBy condition would have to be changed in only one place. But again: if you expect to use it in one place, moving it to a separate method might not help readers to understand what happens.

That is probably about as good as it gets.

Remember that behind that lambda expression magic happens which (effectively) binds to a Comparer<T> where T depends on the type of the columns being compared.

To make this more terse might make it less efficient. Specifically converting and comparing strings makes it both slower and can get you into trouble (ints sort to 1,2,3,...10,11,... vs their strings to "1","10","11",..."19","2","20","21"...).

A "one-liner" is only elegant if it's behaviour is obvious, otherwise it is obfuscated .

Your code is fine. (IMO;-)

Install NuGet System.Linq.Dynamic and you can pass property name as string to OrderBy like below.

Usage list.AsQueryable().OrderBy("PropertyName1 SortOrder, ropertyName SortOrder") . Where PropertyName will be ColA ColB . And SortOrder will be ASC DESC .

  1. Add using System.Linq.Dynamic;
  2. foreach (var line in lines.AsQueryable().OrderBy(condition? "ColB": "ColA")

For .Net Core install NuGet System.Linq.Dynamic.Core .

  1. Add using System.Linq.Dynamic.Core;
  2. foreach (var line in lines.AsQueryable().OrderBy(condition? "ColB": "ColA")

For a better practice rather than providing PropertyName as string use nameof(Class.Property) like in your case nameof(OrderLines.ColA) . So in case you change ColA property it will show Build error and you will not get run time exception .

  • foreach (var line in lines.AsQueryable().OrderBy(condition? nameof(OrderLines.ColB): nameof(OrderLines.ColA))

As @AlanK have mentioned, the closest we can get for simplifying OrderBy would be something like:

Func<OrderLines, string> selector = (orderLine) => condition ? orderLine.ColB : orderLine.ColA;

List<OrderLines> orderLines = lines.OrderBy(selector);

provided both ColA and ColB are of same data type. Otherwise it wouldn't be efficient due to the overhead of data type conversion.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM