Reputation: 954
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.
Upvotes: 2
Views: 2822
Reputation: 1974
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;-)
Upvotes: 2
Reputation: 313
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.
Upvotes: 0
Reputation: 30512
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.
Upvotes: 4
Reputation: 12629
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
.
using System.Linq.Dynamic;
foreach (var line in lines.AsQueryable().OrderBy(condition ? "ColB" : "ColA")
For .Net Core
install NuGet
System.Linq.Dynamic.Core
.
using System.Linq.Dynamic.Core;
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))
Upvotes: 0