【问题标题】:Sorting a list in .Net by one field, then another按一个字段对.Net中的列表进行排序,然后是另一个
【发布时间】:2011-02-26 00:15:17
【问题描述】:

我有一个对象列表,我需要根据它们的一些属性对其进行排序。这可以很好地按一个字段对其进行排序:

reportDataRows.Sort((x, y) => x["Comment1"].CompareTo(y["Comment1"]));
foreach (var row in reportDataRows) {
   ...
}

我在这里看到很多例子,它们只用一个字段来做到这一点。但是我如何按一个字段排序,然后是另一个?或者如何列出许多字段?似乎使用 LINQ orderby thenby 是最好的,但我对它的了解还不够,无法知道如何使用它。

对于参数,像这样支持任意数量的字段进行排序的东西会很好:

var sortBy = new List<string>(){"Comment1","Time"};

我不想在我的每个应用程序中都编写代码来执行此操作。我计划将此排序代码移动到保存数据的类中,以便它可以执行更高级的操作,例如使用参数列表并隐式识别该字段是日期并将其排序为日期而不是字符串。 reportDataRow 对象包含具有此信息的字段,因此我不必进行任何杂乱的检查来确定该字段是否应该是日期。

【问题讨论】:

  • 也许您应该考虑将其设为实际对象类型的列表,或者reportDataRow,或者创建一个具有您想要的属性的新的、普通的 C# 类 (POCO)。更好的是使用像 LinqToSql 这样的 ORM 或实体框架。一旦你有了一个具有正确类型属性的对象列表(字符串属性、DaetTime 属性等),使用 LINQ OrderBy 就很简单了,大多数人认为仅使用 LINQ 内联提供的可读性比尝试抽象它更可取收拾东西。
  • 我让它完全按照我的意图工作,但是谢谢。

标签: c# linq sorting


【解决方案1】:

是的,我认为使用OrderByThenBy 更有意义:

foreach (var row in reportDataRows.OrderBy(x => x["Comment1"]).ThenBy(x => x["Comment2"]) 
{
    ...
}

这假设您要订购的另一件事是“Comment2”。

【讨论】:

  • 这不对列表进行排序,它返回一个新的排序器 IEnumrable
  • @Albin,是的,我知道。但是,如果您查看 OP 的示例,它会工作得很好。 foreach 将对枚举进行操作,该枚举按照 OP 的需要精确排序。
  • foreach (var row in reportDataRows.OrderBy(x => x["Comment1"]).ThenBy(x => Convert.ToDateTime(x["Time"]))) { 这正是我需要它做什么,但我不想在这里做,我想在对象中隐式做。虽然我可能会弄清楚,但我会根据需要更新它。
【解决方案2】:

试试这个:

reportDataRows.Sort((x, y) =>
{
    var compare = x["Comment1"].CompareTo(y["Comment1"]);
    if(compare != 0)
        return compare;

    return x["Comment2"].CompareTo(y["Comment2"]);
});

【讨论】:

  • 这是我以前的做法,除了使用常规函数而不是 lambda。我不想编写只对一种方式进行排序的代码。
  • 我个人更喜欢Linq,但是如果你不懂正则表达式或者Linq,你可以像这样轻松排序。
  • +1 用于实际排序列表而不是返回另一个排序的集合
【解决方案3】:

您可能想查看this previous answer,我在其中发布了一个在 LINQ 中处理多个 order by 的扩展方法。这允许这种语法:

myList.OrderByMany(x => x.Field1, x => x.Field2);

【讨论】:

    【解决方案4】:

    查看 msdn 上 ThenBy 的示例。

    【讨论】:

    • @Albin - 对 2 个正确答案的同样毫无意义的评论。有更好的主意吗?发布吧!
    【解决方案5】:

    如果您要比较自己的对象,则可以实现IComparable 接口。
    否则,您可以使用IComparer 接口。

    【讨论】:

      【解决方案6】:

      使用 LINQ 方法语法:

      var sortedRows = reportDataRows.OrderBy(r => r["Comment1"])
                         .ThenBy(r => r["AnotherField"];
      
      foreach (var row in sortedRows) {
         ...
      }
      

      使用查询理解语法甚至更具可读性:

      var sortedRows = from r in reportDataRows
                       orderby r["Comment1"], r["Comment2"]
                       select r;
      foreach (var row in sortedRows) {
         ...
      }
      

      【讨论】:

        【解决方案7】:

        你明白了。 Enumerable.OrderBy().ThenBy() 是你的票。它的工作原理与外观完全相同;元素按每个投影进行排序,通过比较下一个投影来决定平局。您可以根据需要链接任意数量的 ThenBy,还有 OrderByDesc 和 ThenByDesc 方法可以按降序对该投影进行排序。

        正如 Albin 所指出的,除非您将排序结果分配回原始变量,否则 OrderBy 链不会触及原始列表,如下所示:

        reportDataRows = reportDataRows.OrderBy(x=>x.Comment1).ThenBy(x=>x.Comment2).ToList();
        

        通常,OrderBy 的执行速度会比 List.Sort() 稍慢;该算法旨在处理任何 IEnumerable 元素系列,因此为了排序(这需要知道该系列的每个元素),它会将其整个源可枚举元素放入一个新数组中。但是,OrderBy 与 Sort 相比具有明显的优势,因为它是一种“稳定”的排序;彼此完全相等的元素将在已排序的枚举中保留它们的“相对顺序”(您在遍历未排序列表时遇到的两个中的第一个将是遍历排序列表时遇到的两个中的第一个)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-06-01
          • 2012-02-22
          • 2011-01-25
          相关资源
          最近更新 更多