【问题标题】:LINQ OrderBy only on second propertyLINQ OrderBy 仅在第二个属性上
【发布时间】:2016-02-10 09:07:23
【问题描述】:

我有一个类似的列表

| column1 | column2 |
|    1    |   72    |
|    2    |   30    |
|    3    |   27    |
|    3    |   38    |
|    4    |   72    |

如您所见,列表已在 column1 上排序,我的目标是仅在等于列 1 上对第二列执行 OrderByDescending。基本上,我想要

| column1 | column2 |
|    1    |   72    |
|    2    |   30    |
|    3    |   38    |
|    3    |   27    |
|    4    |   72    |

我无法重新运行第一个 OrderBy(这很难解释,我们不在乎,我只是不能 :D)所以忘记了
list.OrderBy(e => e.column1).ThenByDescending(e => e.column2)

事实上,如果我可以简单地执行.ThenByDescending(e => e.column2) 而不必执行.OrderBy,我不会有任何问题(也许我可以运行一个不会改变排序的“空”OrderBy?然后我就可以做ThenByDescending?)

【问题讨论】:

  • 什么?在colum2 排序之前,您不能在column1 上订购吗?我不知道我是否理解这一点,但是我没有看到任何其他解决方案。无论如何,您不会“重新运行”任何东西,这两个语句会在需要时延迟执行。
  • 如果你不能做一个明显的解决方案,那么你必须努力......复制新列表中的所有column2值并对该列表进行排序并合并新列表和旧列表
  • 你应该解释为什么这不是一个选项,因为它正是要走的路。
  • 好吧,我在这里尽量简单,但我要处理的对象不是包含 2 列的列表,而是包含对象的查询 (IQueryable)。该查询来自一个基于参数处理排序的通用函数。所以当我面前有我的查询时(在调用函数之后),我不知道它在哪一列上排序,我只需要管理第二次排序。
  • 好吧,我假设我可以查看发送给函数以重新运行第一个 OrderBy 的参数,我想避免这个解决方案,但似乎我必须这样做方式

标签: c# linq


【解决方案1】:

list.OrderBy(e => e.column1).ThenByDescending(e => e.column2) 还是要走的路。

算法必须知道它必须首先对e.column1 进行排序,无论它是否真的改变了某些东西。它必须知道它只需要对e.column2在第一个排序语句的子集中进行排序。你不能通过在column2 上“仅”排序来做到这一点。

【讨论】:

    【解决方案2】:
    list.GroupBy(i => i.column1).SelectMany(i => i.OrderByDescending(g => g.column2))
    

    将与许多供应商合作,但有些供应商可能无法保留GroupBy 中的顺序。在这种情况下:

    list.AsEnumerable().GroupBy(i => i.column1).SelectMany(i => i.OrderByDescending(g => g.column2))
    

    将通过将操作强制到内存中来工作(其中排序由GroupBy 保留),但缺点是所有后续操作都在内存中而不是在数据库等上完成。

    【讨论】:

      【解决方案3】:

      如果您确实有一个列表而不是一个 IEnumerable,那么实际上可以使用 the overload of List.Sort() which lets you specify a subset of items to sort, along with a comparer 来做到这一点。

      您要做的是对列表进行 O(N) 次遍历,以根据已排序的列确定每个子组出现的位置。然后根据二级排序列对多个项目的每个子组进行排序。

      在选择用于识别子组的键和用于对子组进行排序的键时,会涉及到一些繁琐的工作。

      这是实现:

      public static void SortSubgroupsBy<T>
      (
          List<T> items, 
          Func<T, T, bool> sortedColumnComparer,  // Used to compare the already-sorted column.
          Func<T, T, int>  unsortedColumnComparer // Used to compare the unsorted column.
      )
      {
          var unsortedComparer = Comparer<T>.Create(
              (x, y) => unsortedColumnComparer(x, y));
      
          for (int i = 0; i < items.Count; ++i)
          {
              int j = i + 1;
      
              while (j < items.Count && sortedColumnComparer(items[i], items[j]))
                  ++j;
      
              if ((j - i) > 1)
                  items.Sort(i, j-i, unsortedComparer);
          }
      }
      

      这是控制台应用程序中的完整演示:

          using System;
          using System.Collections.Generic;
      
          namespace ConsoleApplication1
          {
              class Item
              {
                  public Item(int column1, int column2)
                  {
                      Column1 = column1;
                      Column2 = column2;
                  }
      
                  public int Column1;
                  public int Column2;
      
                  public override string ToString()
                  {
                      return $"[{Column1}, {Column2}]";
                  }
              }
              class Program
              {
                  static void Main()
                  {
                      List<Item> items = new List<Item>
                      {
                          new Item(1, 72),
                          new Item(2, 29),
                          new Item(2, 30),
                          new Item(3, 27),
                          new Item(3, 38),
                          new Item(3, 53),
                          new Item(4, 72),
                          new Item(4, 21),
                          new Item(4, 86),
                          new Item(4, 17),
                          new Item(5, 90)
                      };
      
                      SortSubgroupsBy(
                          items, 
                          (x, y) => x.Column1 == y.Column1, // Compare sorted column.
                          (x, y) => y.Column2 - x.Column2); // Compare unsorted column.
      
                      Console.WriteLine(string.Join("\n", items));
                  }
      
                  public static void SortSubgroupsBy<T>
                  (
                      List<T> items, 
                      Func<T, T, bool> sortedColumnComparer,  // Used to compare the already-sorted column.
                      Func<T, T, int>  unsortedColumnComparer // Used to compare the unsorted column.
                  )
                  {
                      var unsortedComparer = Comparer<T>.Create(
                          (x, y) => unsortedColumnComparer(x, y));
      
                      for (int i = 0; i < items.Count; ++i)
                      {
                          int j = i + 1;
      
                          while (j < items.Count && sortedColumnComparer(items[i], items[j]))
                              ++j;
      
                          if ((j - i) > 1)
                              items.Sort(i, j-i, unsortedComparer);
                      }
                  }
              }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-09
        • 1970-01-01
        相关资源
        最近更新 更多