在这篇SO文章中:ascending/descending in LINQ - can one change the order via parameter?提出了一个解决方案,用于如何按升序或降序有条件地排序。
文章未涉及的内容是,如果我想根据查询中的数据做出决定(实际上,最有可能的是什么)?
我似乎无法做到:
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)
并将它们连接起来
答案 0 :(得分:1)
我为此写了一个通用的扩展方法:
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");
希望它有所帮助。
丹尼尔
答案 1 :(得分:1)
我没有您的实体/对象,但您不能只做以下事情:
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;
也就是说,创建一个新对象,其中包含可以执行排序的单个属性,然后在您的订购操作中使用它?
答案 2 :(得分:1)
一种方法是将信息提取到更有意义的排序结构中:
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,然后它可以按升序排序)。
答案 3 :(得分:1)
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进行排序。
答案 4 :(得分:0)
您可以尝试自定义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));
答案 5 :(得分:0)
我认为一般来说,您很难实施提议的OrderByWithDirection
。结果会是什么?数据集{1, true}, {2, false}, {3, true}, {4, false}
?它适用于你的情况的唯一原因是你的第二种与你的第一种有很高的相关性,即第二种不需要比较“出价”和“询问”。
解决此问题的最佳方法是使用实现整个排序的自定义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;
}
}