对 LINQ 中的路径列表进行排序?

发布于 2024-11-01 17:08:07 字数 1039 浏览 1 评论 0原文

假设我有以下文件夹:

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)

任何帮助将不胜感激!

Let say I have the following folders:

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

And a query

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

The results:

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)

Is there anyway to sort the list to the right order? I expected it to be:

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)

Any helps would be appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

独木成林 2024-11-08 17:08:07

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

class DirectorySorter : IComparer<string>
{
    public int Compare(string x, string y)
    {
        return 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 < minLength; i++)
        {
            var ires = xPaths[i].CompareTo(yPaths[i]);
            if (ires != 0) return ires;
        }
        var lres = xPaths.Length.CompareTo(yPaths.Length);
        if (lres == 0)
        {
            return lres;
        }
        else if (lres < 0)
        {
            var i = y.LastIndexOf(Path.DirectorySeparatorChar);
            return x.Length == i ? lres : -lres;
        }
        else //if (lres > 0)
        {
            var i = x.LastIndexOf(Path.DirectorySeparatorChar);
            return y.Length == i ? lres : -lres;
        }
    }
}


(看到 Steck 的答案表明我几乎已经达到了最初的水平。只是我需要使用序数字符串比较器。所以事实证明它可以使用该更改。)

另一方面,我们可以使用目录结构来简化此任务并且不实现比较器。

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

This wasn't as trivial as I thought. Probably the most sane solution (aside from building the list recursively) is to implement a comparer for this to do the sorting.

class DirectorySorter : IComparer<string>
{
    public int Compare(string x, string y)
    {
        return 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 < minLength; i++)
        {
            var ires = xPaths[i].CompareTo(yPaths[i]);
            if (ires != 0) return ires;
        }
        var lres = xPaths.Length.CompareTo(yPaths.Length);
        if (lres == 0)
        {
            return lres;
        }
        else if (lres < 0)
        {
            var i = y.LastIndexOf(Path.DirectorySeparatorChar);
            return x.Length == i ? lres : -lres;
        }
        else //if (lres > 0)
        {
            var i = x.LastIndexOf(Path.DirectorySeparatorChar);
            return y.Length == i ? lres : -lres;
        }
    }
}

(Seeing Steck's answer shows that I was nearly there with what I originally had. Just that I needed to use the Ordinal string comparer. So it turns out it works using that change.)

On the other hand, we could use some properties of the directory structure to simplify this task and not implement a comparer.

var query = Directory
    .EnumerateDirectories(@"D:\Project\uploads", "*", SearchOption.AllDirectories)
    .OrderBy(name => name.Replace(Path.DirectorySeparatorChar, '\0'), StringComparer.Ordinal);
溺ぐ爱和你が 2024-11-08 17:08:07
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();
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'));
    }
}

and then

var source = Directory.GetDirectories(@"D:\Project\uploads", "*.*", SearchOption.AllDirectories)
var target = source.OrderBy(x => x, new Comparer()).ToArray();
娇女薄笑 2024-11-08 17:08:07

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

  • 如果您找到一种方法将字符串中的 \ 替换为比所有其他字符都小的字符,并使用此替换字符串作为键。

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

The only thing you need to change about the default ordering is to make sure that the \ character is always treated as the first letter in your alphabet. I don't have an exact answer how to implement this, but:

  • You can use order by clause if you find a way to replace \ in the string with a character that is smaller than all other characters and use this replaced string as the key.

  • You can use Array.Sort and implement your string comparer that re-implements string comparison, but encodes this additional rule about the \ character.

海拔太高太耀眼 2024-11-08 17:08:07

使用.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

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

With .NET 4.0 try

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

it might do what you expect. If it doesn't, you can do it explicitly:

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

Lastly you might do something like:

 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

to get even more control/flexibility. Chose the simplest approach depending on your needs

软糖 2024-11-08 17:08:07

感谢您的评论和回答,

我认为递归的生活会更容易

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();
}

Thanks for ur comment and answer guys,

I think life'll be much easier with recursive

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();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文