在这篇SO文章中:LINQ中的升序/降序-可以通过参数更改顺序吗?给出了如何按升序或降序进行有条件排序的解决方案。
这篇文章没有涵盖的是,如果我想根据查询中的数据做出决定(实际上,最有可能的是什么),该怎么办?
我似乎做不到:
source.OrderByWithDirection(o => o.MyColumnToOrderBy, o => o.SortingDirection)
那么我需要重写这些方法吗?怎样
-更新-
根据原理,以下是我想要实现的目标:
Strike Type Price
98 Ask 101
98 Ask 100
98 Ask 99
98 Bid 95
98 Bid 96
98 Bid 97
正如您所看到的,请求是按顺序排列的,但出价是按顺序排序的,这样差异最大的行彼此相邻。所以我想说的是:
source.OrderBy(o => new { o.Strike, o.Type })
.ThenByWithDirection(o => o.Price, o => o.Type == "Ask" ? __down__ : __up__)
-更新II-
要做到这一点,一种笨拙的方法是发出两个单独的查询,如下所示:
source
.Where(o => o.Type == "Ask")
.OrderBy(o => new { o.Strike, o.Type })
.ThenBy(o => o.Price)
source
.Where(o => o.Type == "Bid")
.OrderBy(o => new { o.Strike, o.Type })
.ThenByDescending(o => o.Price)
并将它们连接到
我没有你的实体/对象,但你能不能不只是做一些类似的事情:
var items = from t in source
select new { a = t, b = t.Type == "Ask" ? -t.Price : t.Price };
var sorted = from x in items
orderby x.a.Type, x.b
select x.a;
也就是说,创建一个包含单个属性的新对象,可以通过该属性执行排序,然后在排序操作中使用该属性?
我为此编写了一个通用扩展方法:
public static IEnumerable<T> OrderByString<T>(this IEnumerable<T> entities, string sortExpression)
{
bool descending = sortExpression.ToLowerInvariant().Trim().EndsWith("desc");
PropertyInfo[] propertyInfos = typeof(T).GetProperties();
// sort properties by name
foreach (PropertyInfo propertyInfo in propertyInfos)
{
if (sortExpression.Split(' ')[0] == propertyInfo.Name)
{
PropertyInfo info = propertyInfo;
if (descending)
return entities.OrderByDescending(c => info.GetValue(c, null));
else
return entities.OrderBy(c => info.GetValue(c, null));
}
}
return entities;
}
用法:
IEnumerable x = cars.OrderByString("color desc");
希望能有所帮助。
Daniel
一种方法是将信息提取到更有意义的排序结构中:
source
.Select(o => new {
o.Strike,
o.Type,
o.Price,
o.AskPrice = o.Type == "Ask" ? o.Price : (int)null,
o.BidPrice = o.Type == "Bid" ? o.Price : (int)null
})
.OrderBy(o => o.Strike)
.ThenBy(o => o.Type)
.ThenByDescending(o => o.AskPrice)
.ThenBy(o => o.BidPrice)
这假设你知道这是你想要排序的两件事
另一种选择是反转其中一个值(例如,出于排序目的,使用1000000-AskPrice,然后可以升序排序)。
IComparer的答案很接近,但需要这样:
public class BidComparer : IComparer<BidDatum> {
public int Compare(BidDatum x, BidDatum y) {
return (x.Type != y.Type) ? (x.Type.CompareTo(y.Type)) : ((x.Type == "Ask") ? y.Price.CompareTo(x.Price) : x.Price.CompareTo(y.Price));
}
}
现在你的Linq可以是这样的:
var ans = srcData.OrderBy(d => d.Strike).ThenBy(d => d, new BidComparer());
我跳过了一个.ThenBy(d => d.Type)
,因为自定义IComparer也会在出价前对Ask进行排序。
您可以尝试自定义IComparer
:
public class MyComparer<T> : IComparer<T> {
public MyComparer(bool ascending){
Descending = !ascending;
}
public bool Descending {get;set;}
public int Compare(T x, T y){
//your normal logic
int result = ....;
return Descending ? -result : result;
}
}
然后在您的OrderBy
查询中使用它:
source.OrderBy(o=>o.MyColumnToOrderBy, new MyComparer(true));//true for ascending and false for descending
更新
您可以使用一些enum
而不是bool
来指示排序方向:
public class MyComparer<T> : IComparer<T> {
public MyComparer(SortingDirection sortDir){
Direction = sortDir;
}
public SortingDirection Direction {get;set;}
public int Compare(T x, T y){
//your normal logic
int result = ....;
return Direction == SortingDirection.Descending ? -result : result;
}
public enum SortingDirection {
Ascending,
Descending
}
}
//o.SortingDirection should be type of enum SortingDirection
source.OrderBy(o=>o.MyColumnToOrderBy, new MyComparer(o.SortingDirection));
围绕的另一项工作
我认为你可以用GroupBy
的一个小技巧来通过o.SortingDirection
订购,比如:
source.GroupBy(x=>x.MyColumnToOrderBy)
.SelectMany(g=> o.SortingDirection == SortingDirection.Ascending ?
g.OrderBy(a=>a.Key) :
g.OrderByDescending(a=>a.Key));
我认为通常情况下,您将很难实现您提出的OrderByWithDirection
。例如,数据集{1, true}, {2, false}, {3, true}, {4, false}
的结果会是什么?在你的情况下,它有效的唯一原因是你的第二种排序与第一种排序有很高的相关性,即第二种分类永远不需要比较"出价"one_answers"要价"。
最好的方法是使用一个自定义的IComparator来实现整个排序
int Compare(Entry a, Entry b)
{
if (a.Type == b.Type)
{
// same type, look at the price difference and reverse if they are both Bids
return a.Price.CompareTo(b.Price) * (a.Type == "Bid" ? -1 : 1);
}
else
{
// different types, Ask comes first
return (a.Type == "Ask") ? -1 : 1;
}
}