【问题标题】:Sorting a generic list by an external sort order按外部排序顺序对通用列表进行排序
【发布时间】:2013-01-09 21:43:11
【问题描述】:

我有一个通用列表

简化示例

var list = new List<string>()
  {
    "lorem1.doc",
    "lorem2.docx",
    "lorem3.ppt",
    "lorem4.pptx",
    "lorem5.doc",
    "lorem6.doc",
  };

我想做的是根据外部列表排序对这些项目进行排序

举例

var sortList = new[] { "pptx", "ppt", "docx", "doc" };

// Or
var sortList = new List<string>() { "pptx", "ppt", "docx", "doc" };

linq 中是否有任何内置功能可以帮助我实现这一目标,还是我必须采用 foreach 方式?

【问题讨论】:

  • 您想对它们进行排序并将它们保存在一个列表中,还是返回组有用?

标签: c# linq


【解决方案1】:

有了这个列表,你可以使用IndexOf 代替Enumerable.OrderBy

var sorted = list.OrderBy(s => sortList.IndexOf(Path.GetExtension(s)));

所以sortList中的扩展索引决定了其他列表中的优先级。未知扩展具有最高优先级,因为它们的索引为 -1。

但您需要在扩展中添加一个点才能使其正常工作:

var sortList = new List<string>() { ".pptx", ".ppt", ".docx", ".doc" };

如果这不是一个选项,您必须摆弄SubstringRemove,例如:

var sorted = list.OrderBy(s => sortList.IndexOf(Path.GetExtension(s).Remove(0,1)));

【讨论】:

  • 请注意 GetExtension() 还返回扩展名“点”,而 sortList 没有它...
  • @digEmAll 它正在从原始列表中的文件名中获取扩展名(例如,“lorem1.doc”)
  • 是的,但问题是GetExtension 将返回类似.ppt 的内容,因此不会在sortedList 中找到。这样的事情解决了System.IO.Path.GetExtension(s).Substring(1)。 (或者,更简单 - 只需在 sortedList 中添加点)
  • @ChrisSinclair: Path.GetExtension("lorem1.doc") 返回的 ".doc" 不在 sortList 中,所以 IndexOf 返回 -1 ...
  • @Tim - at the colon 应该是 add the dot 否?
【解决方案2】:

即使某些文件名没有扩展名,此解决方案也可以工作:

var sortList = new List<string>() { "pptx", "ppt", "docx", "doc" };
var list = new List<string>()
  {
    "lorem1.doc",
    "lorem2.docx",
    "lorem3.ppt",
    "lorem4.pptx",
    "lorem5.doc",
    "lorem6.doc",
  };

var result = 
       list.OrderBy(f => sortList.IndexOf(Path.GetExtension(f).Replace(".","")));

【讨论】:

    【解决方案3】:

    您可以尝试使用 Array.IndexOf() 方法:

    var sortedList = list.OrderBy(i => sortList.IndexOf(System.IO.Path.GetExtension(i))).ToList();
    

    【讨论】:

      【解决方案4】:

      sortDicionary 会更有效率:

      var sortDictionary = new Dictionary<string, int> {
          { ".pptx", 0 },
          { ".ppt" , 1 },
          { ".docx", 2 },
          { ".doc" , 3 } };
      
      var sortedList = list.OrderBy(i => {
          var s = Path.GetExtension(i);
          int rank;
          if (sortDictionary.TryGetValue(s, out rank))
              return rank;
          return int.MaxValue; // for unknown at end, or -1 for at start
      });
      

      这样查找是O(1) 而不是O(# of extensions)

      另外,如果您有大量文件名和少量扩展名,这样做实际上可能会更快

      var sortedList = list
          .GroupBy(p => Path.GetExtension(p))
          .OrderBy(g => {
              int rank;
              if (sortDictionary.TryGetValue(g.Key, out rank))
                  return rank;
              return int.MaxValue; // for unknown at end, or -1 for at start
          })
          .SelectMany(g => g);
      

      这意味着排序按输入中不同扩展名的数量而不是输入中的项目数进行缩放。

      这还允许您为两个扩展赋予相同的优先级。

      【讨论】:

        【解决方案5】:

        这是不使用OrderBy的另一种方式:

        var res = 
        sortList.SelectMany(x => list.Where(f => Path.GetExtension(f).EndsWith(x)));
        

        请注意,这种方法的复杂性是O(n * m)n = sortList.Countm list.Count

        OrderBy 方法的最坏情况复杂度是O(n * m * log m),但通常它可能会更快(因为IndexOf 并不总是导致O(n))。但是,对于小的 nm,您不会注意到任何区别。

        对于大型列表,最快的方法(复杂性 O(n+m))可能是构建临时查找,即:

        var lookup = list.ToLookup(x => Path.GetExtension(x).Remove(0,1));
        var res = sortList.Where(x => lookup.Contains(x)).SelectMany(x => lookup[x]);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-08-07
          • 2014-11-29
          • 2017-01-08
          • 2017-04-08
          • 1970-01-01
          • 2013-05-02
          • 2014-02-08
          • 2018-06-05
          相关资源
          最近更新 更多