如果存在平局,我如何指定使用Linq ThenBy子句?

时间:2012-05-22 03:37:16

标签: c# .net linq sorting ienumerable

我有一个与OrderBy和ThenBy

的linq查询(不是数据库相关的)
var sortedList = unsortedList
                .OrderBy(foo => foo.Bar) //this property access is relatively fast
                .ThenBy(foo => foo.GetCurrentValue()) //this method execution is slow

获取foo.Bar的速度很快,但执行foo.GetCurrentValue()非常慢。返回值仅在某些成员具有相等的Bar值时才重要,这种情况很少发生,但在发生这种情况时需要考虑。是否可以选择仅在需要在相等Bar值的情况下进行平局时执行ThenBy子句? (即如果foo.Bar值是唯一的,则不会执行。)

另外,实际上Bar也有点慢,所以最好不要为同一个对象调用它两次。

5 个答案:

答案 0 :(得分:5)

由于您不在数据库中,并且需要严格控制排序,因此您可以将单个OrderBy与自定义IComparer一起使用,该IComparer仅访问它所需的内容,并且不执行不必要的评估。

答案 1 :(得分:4)

这有点笨拙,但我确信它可以改进 - 也许它不会在一个linq语句中完成,但它应该有效:

var sortedList2 = unsortedList
                .OrderBy(foo => foo.Bar)
                .GroupBy(foo => foo.Bar);

            var result = new List<Foo>();
            foreach (var s in sortedList2)
            {
                if (s.Count() > 1)
                {
                    var ordered = s
                        .OrderBy(el => el.GetCurrentValue());
                    result.AddRange(ordered);
                }
                else
                {
                    result.AddRange(s);
                }
            }

更新:
我们可以争论这是否有所改善,但它至少看起来更简洁:

var list3 = (from s in sortedList2
             let x = s.Count()
             select x == 1 
                    ? s.Select(el => el) 
                    : s.OrderBy(el => el.GetCurrentValue()))
             .SelectMany(n => n);

<强> UPDATE2:

您可以使用Skip(1).Any()代替Count() - 这应该避免我猜测整个序列的枚举。

答案 2 :(得分:1)

var query = unsortedList
  .GroupBy(foo => foo.Bar)
  .OrderBy(g => g.Key)
  .SelectMany(g => g.Skip(1).Any() ? g.OrderBy(foo => foo.GetCurrentValue()) : g);

这有明显的缺点,即不返回IOrderedEnumerable<Foo>

答案 3 :(得分:0)

试试这个

var sortedList = unsortedList.OrderBy(foo => foo.Bar);

if(some_Condition)
{ 
   sortedList = sortedList.OrderBy(foo => foo.GetCurrentValue()); 
}

答案 4 :(得分:0)

我改变了Joanna Turban的解决方案并开发了以下扩展方法:

public static IEnumerable<TSource> OrderByThenBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> orderBy, Func<TSource, TKey> thenBy)
{
    var sorted = source
        .Select(s => new Tuple<TSource, TKey>(s, orderBy(s)))
        .OrderBy(s => s.Item2)
        .GroupBy(s => s.Item2);

    var result = new List<TSource>();
    foreach (var s in sorted)
    {
        if (s.Count() > 1)
            result.AddRange(s.Select(p => p.Item1).OrderBy(thenBy));
        else
            result.Add(s.First().Item1);
    }
    return result;
}