【问题标题】:Doing multiple sorts across more than one line of code?对不止一行代码进行多种排序?
【发布时间】:2011-08-19 15:55:52
【问题描述】:

我有一个数组,用于存储我想要对列表进行排序的顺序。

SortOrderArray: "Color", "Volume", "Weight"

所以我想按颜色、体积和重量来排列我的清单

MyList.OrderBy(a=>a.Color).ThenBy(a=>a.Volume).ThenBy(a=>a.Weight).ToList();

所以这很好。现在,我希望能够编写一个函数,根据我发送的 sortOrder 数组进行排序:

public List<row> GetSortedList(List<row> list, string[] sortOrder){
    ???
}

如果不为 sortOrders 的每个组合编写一个 linq 查询,我无法弄清楚如何做到这一点(27 个不同的查询似乎是实现这一目标的最糟糕的方法,而且我犯一个小错误的可能性很大) .我希望能够只编写 3 个 linq 查询,根据 3 种排序方法中的每一种重新排序列表,如下所示:

switch(sortOrder[0]){
    Sort by the first sort method
}  
switch(sortOrder[1]){
    Sort by the second sort method
}
switch(sortOrder[2]){
    Sort by the third sort method
}

但是如果我尝试执行上面的代码,它只是每次都使用它,而不是在它上面的代码之后进行子排序。希望这很清楚,任何帮助将不胜感激。

【问题讨论】:

    标签: c# linq sorting


    【解决方案1】:

    两件事。您需要一种执行“稳定排序”的排序方法 - 它使用相同的键保持项目的现有顺序。然后你需要按照排序条件的相反顺序调用它,以便主排序是你做的最后一个。

    【讨论】:

    • 这是一个聪明的解决方案,你知道Linq的order by是否“稳定”吗?此外,“稳定排序”的正确术语是什么(或者仅仅是它)
    • 看来“OrderBy”是稳定的:见this question。而"Stable Sort" 是常用词。
    【解决方案2】:

    如果要排序的可能字段数量有限,切换可能是最佳解决方案。如果您正在寻找可扩展的东西,您必须 generate a lambda expression on the fly,并使用反射来调用适当类型的 .OrderBy.ThenBy 方法。

    【讨论】:

      【解决方案3】:

      使用Dynamic LINQ,您可以执行以下操作:

      public List<row> GetSortedList(List<row> list, string[] sortOrder)
      {
          // argument-validation, including testing that 
          // sort-order has at least 1 item.
      
          return sortOrder.Skip(1)
                          .Aggregate(list.AsQueryable().OrderBy(sortOrder.First()),
                                     (query, nextSortTerm) => query.ThenBy(nextSortTerm))
                          .ToList();     
      }
      

      本质上:OrderBy 第一个排序项,ThenBy 剩下的。

      编辑:添加了一个 AsQueryable 调用以使动态 LINQ 在 IEnumerable&lt;T&gt; 上工作

      【讨论】:

      • 这看起来很酷,但是我认为是字符串的sortorder 的第一个元素如何转换为Func&lt;T, TKey&gt;(即KeySelector)?
      • @Smudge202:这就是 Dynamic LINQ 的作用。
      【解决方案4】:
      【解决方案5】:

      我猜,你没有使用订单子句的返回值。

      public List<row> GetSortedList(List<row> list, string[] sortOrder)
      {
          IOrderedEnumerable<row> result = null;
      
          bool first = true;
      
          foreach(sortClause in sortOrder)
          {
              switch sortClause
              {
                  case "Color":
                      if(first)
                          result = list.OrderBy(x => x.Color);
                      else
                          result = result.ThenBy(x => x.Color);
                      break;
                  // the other cases 
              }
              first = false;
          }    
      
          return result.ToList();
      }
      

      类似的东西。

      【讨论】:

      • 看起来我只能在 .OrderBy 后面放一个 .ThenBy,不幸的是,这段代码对我不起作用...
      • @sooprise:然后您在某处引入了错误。这行得通。 first 的全部意义在于,您首先需要一个 OrderBy,然后才能使用 ThenBy。请使用您使用的确切代码更新您的问题。
      • 我的代码与你完全相同,但我使用的是 for 而不是 foreach 并检查 i==0 而不是 if first==true。
      • @sooprise:我修复了代码,因为它甚至没有编译。现在它应该可以正常工作了。请注意:OrderBy 需要在list 上调用,所有ThenBys 都需要在result 上调用。我对其进行了测试,它按预期工作。您可以将this 代码粘贴到 LINQPad 中进行验证。
      猜你喜欢
      • 2013-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-25
      • 2012-01-02
      • 1970-01-01
      • 1970-01-01
      • 2020-03-20
      相关资源
      最近更新 更多