【问题标题】:How to order IEnumerable based on a predefined sequence?如何根据预定义的序列订购 IEnumerable?
【发布时间】:2014-01-10 21:29:08
【问题描述】:

我有一个IEnumerable<Process>

public class Process
{
    public string Id { get; set; }
    public string Name { get; set; }
    public int Type { get; set; }
}

我想根据Type 订购这个IEnumerable<Process>。排序应基于预定义的顺序。

顺序是:7,3,4,1。因此,如果集合具有这些值中的任何一个,它们应该按顺序出现,首先是 7,然后是 3,以此类推。

任何其他 Type 然后打开应该按升序排列。

根据预定义的顺序排序的正确方法是什么?我应该将序列本身定义为enum吗?

【问题讨论】:

    标签: c# linq c#-4.0 ienumerable


    【解决方案1】:

    如果数字序列是List<int>,您可以按索引排序:

    var ordered = processes
        .OrderByDescending(p => numbers.Contains(p.Type))
        .ThenBy(p => numbers.IndexOf(p.Type));
    

    或者,使用 LINQ 的查询语法更高效、更易读:

    var ordered = from proc in processes
                  let index = numbers.IndexOf(proc.Type)
                  orderby index == -1, index
                  select proc;
    

    index == -1 返回一个bool,其中true“高于”false。这就是全部技巧。

    顺便说一句,Array 还有一个IndexOf 方法:

    let index = Array.IndexOf(numArray, proc.Type)
    

    【讨论】:

      【解决方案2】:

      创建从这些项目到要排序的值(在本例中为该列表中的索引)的查找,然后您可以轻松地将每个值投影到要排序的值:

      var typeOrders = new int[]{7,3,4,1};
      var typeOrderLookup = typeOrders.Select((type,i)=>new{type,i})
          .ToDictionary(pair => pair.type, pair => pair.i);
      
      var query = someData.OrderBy(process => typeOrderLookup[process.Type]);
      

      【讨论】:

        【解决方案3】:

        我从你那里得到的问题是,如果你的进程列表在sequence = (7, 3, 4, 1) 中包含任何ProcessType,那么这些进程应该首先出现在基于sequence 的排序上,然后是其余的进程列表应根据Type 升序排列。 我所做的基本上是创建两个列表,最后合并它们。第一个列表仅包含sequence 中具有Types 的进程,该进程基于序列进行排序,第二个列表包含原始列表的其余部分,仅按Type 排序。

        这是您的示例数据答案:

        List<Process> processList = new List<Process>() 
        {
            new Process() { Id = "1", name = "1", Type = 7},
            new Process() { Id = "2", name = "2", Type = 1},
            new Process() { Id = "3", name = "3", Type = 4},
            new Process() { Id = "4", name = "4", Type = 3},
            new Process() { Id = "5", name = "5", Type = 9},
            new Process() { Id = "6", name = "6", Type = 8},
        };
        
        List<int> sequence = new List<int>() { 7, 3, 4, 1 };
        var pl = processList.Where(p => sequence.Contains(p.Type))
                                                .OrderBy(p => sequence.IndexOf(p.Type))
                            .Union(processList.Where(p => !sequence.Contains(p.Type))
                                              .OrderBy(p => p.Type));
        

        【讨论】:

          【解决方案4】:

          我们可以通过一个表达式来做到这一点:

          var order = new int[] { 7, 3, 4, 1 };
          var orderedProccesses = order.Select(i => processes.FirstOrDefault(p => p.Type == i))
              .Where(p => p != null)
              .Concat(processes.Where(p => !order.Contains(p.Type)).OrderBy(p => p.Type))
              .ToArray();
          

          首先将定义的订单列表映射到对应的进程:

          order.Select((i) => processes.FirstOrDefault(p => p.Type == i))
          

          然后,进行完整性检查,因为不能保证订单中的每个项目都有相应的流程:

              .Where((p) => p != null)
          

          然后添加其余的进程并按类型对其进行排序

              .Concat(processes.Where((p) => !order.Contains(p.Type)).OrderBy((p) => p.Type))
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2020-12-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-09-13
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多