【问题标题】:Sort a list of path in LINQ?对 LINQ 中的路径列表进行排序?
【发布时间】:2011-08-06 04:06:23
【问题描述】:

假设我有以下文件夹:

New Folder
- New Folder
- New Folder (2)
- New Folder (3)
- New Folder (4)
New Folder (2)
New Folder (3)
New Folder (4)

还有一个查询

from s in Directory.GetDirectories(@"D:\Project\uploads", "*.*", SearchOption.AllDirectories)
select s

结果:

D:\Project\uploads\New Folder
D:\Project\uploads\New Folder (2)
D:\Project\uploads\New Folder (3)
D:\Project\uploads\New Folder (4)
D:\Project\uploads\New Folder\New Folder
D:\Project\uploads\New Folder\New Folder (2)
D:\Project\uploads\New Folder\New Folder (3)
D:\Project\uploads\New Folder\New Folder (4)

无论如何要按正确的顺序对列表进行排序吗?我希望它是:

D:\Project\uploads\New Folder
D:\Project\uploads\New Folder\New Folder
D:\Project\uploads\New Folder\New Folder (2)
D:\Project\uploads\New Folder\New Folder (3)
D:\Project\uploads\New Folder\New Folder (4)
D:\Project\uploads\New Folder (2)
D:\Project\uploads\New Folder (3)
D:\Project\uploads\New Folder (4)

任何帮助将不胜感激!

【问题讨论】:

    标签: linq sorting path hierarchy flatten


    【解决方案1】:

    用 .NET 4.0 试试

     Directory.EnumerateDirectories(@"D:\Project\uploads", "*.*", SearchOption.AllDirectories) 
    

    它可能会如你所愿。如果没有,您可以明确执行:

     Directory.GetDirectories(@"D:\Project\uploads")
          .SelectMany(dir => dir.GetDirectories().OrderBy(sub => sub.Name))
    

    最后你可能会做这样的事情:

     from s in Directory.GetDirectories(@"D:\Project\uploads", "*.*", SearchOption.AllDirectories)
     order by s.Parent.Name, s.Name
     select s
    
     from s in Directory.GetDirectories(@"D:\Project\uploads", "*.*", SearchOption.AllDirectories)
     let members = s.Name.Split(new [] {Path.SeparatorChar})
     order by members[2], s.Name
     select s
    

    以获得更多的控制/灵活性。根据您的需要选择最简单的方法

    【讨论】:

    • 嗯,没想到那个。 GetDirectories() 是否保证按排序顺序返回目录?否则你必须对其进行排序。
    • 我认为第二个版本需要递归。
    • @Tomas:我“也可以这么想”,但在任何地方都没有明确说明,让 OP 决定
    【解决方案2】:

    您唯一需要更改的默认顺序是确保\ 字符始终被视为字母表中的第一个字母。我没有确切的答案如何实现这一点,但是:

      1234563 /li>
    • 您可以使用 Array.Sort 并实现您的字符串比较器,以重新实现字符串比较,但对有关 \ 字符的附加规则进行编码。

    【讨论】:

    • 您也可以为此目的破解您的区域设置文化信息。我建议“说出你的意思”(故意编程)
    【解决方案3】:

    这并不像我想象的那么简单。 可能最明智的解决方案(除了递归地构建列表)是为此实现一个比较器来进行排序。

    类 DirectorySorter : IComparer
    {
        公共 int 比较(字符串 x,字符串 y)
        {
            返回 StringComparer.Ordinal.Compare(x.Replace(Path.DirectorySeparatorChar, '\0'),
                                            y.Replace(Path.DirectorySeparatorChar, '\0'));
            var xPaths = x.Split(Path.DirectorySeparatorChar);
            var yPaths = y.Split(Path.DirectorySeparatorChar);
            var minLength = Math.Min(xPaths.Length, yPaths.Length);
            for (int i = 0; i  0)
            {
                var i = x.LastIndexOf(Path.DirectorySeparatorChar);
                返回 y.Length == i ? lres:-lres;
            }
        }
    }
    

    (看到 Steck 的回答表明我已经接近原来的状态了。只是我需要使用 Ordinal 字符串比较器。所以事实证明它可以使用该更改。)

    另一方面,我们可以使用目录结构的某些属性来简化此任务,而不是实现比较器。

    var query = Directory
        .EnumerateDirectories(@"D:\Project\uploads", "*", SearchOption.AllDirectories)
        .OrderBy(name => name.Replace(Path.DirectorySeparatorChar, '\0'), StringComparer.Ordinal);
    

    【讨论】:

    • @NVA:是的,我刚刚注意到这一点……不是我对某些输入的预期顺序。让我看看我能不能调整一下。
    • @Byul:我发现了如何实现这样的比较器。所以如果你想通过排序来做到这一点,你可以使用这个比较器。 p.s. 改名? :)
    【解决方案4】:

    感谢您的评论和回答,

    我认为使用递归会更轻松

    void Main()
    {
        string rootFolder = @"D:\Project\uploads";
    
        string[] f = Directory.GetDirectories(rootFolder, "*.*", SearchOption.AllDirectories);
    
        Func<string, string[]> build = null;
    
        build = (p) => {
            return (from x in f where Path.GetDirectoryName(x) == p
                    from y in new string[]{ x }.Union(build(x)) select y).ToArray();
        };
    
        f = build(rootFolder).Dump();
    }
    

    【讨论】:

      【解决方案5】:
      private class Comparer : IComparer<string>
      {
          public int Compare(string x, string y)
          {
              return StringComparer.Ordinal.Compare(x.Replace(Path.DirectorySeparatorChar, '\0'),
                                                      y.Replace(Path.DirectorySeparatorChar, '\0'));
          }
      }
      

      然后

      var source = Directory.GetDirectories(@"D:\Project\uploads", "*.*", SearchOption.AllDirectories)
      var target = source.OrderBy(x => x, new Comparer()).ToArray();
      

      【讨论】:

      • 天哪,我不知道使用序数比较器有什么不同,虽然那时创建一个新的比较器并不是真的必要。
      • 是的。你说的对。 x => x.Replace(Path.DirectorySeparatorChar, '\0') 是一个很好的键选择器,带有 StringComparer.Ordinal 比较器
      猜你喜欢
      • 2019-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多