Сортировать список путей в LINQ? - PullRequest
0 голосов
/ 15 апреля 2011

Допустим, у меня есть следующие папки:

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)

Любая помощь будет оценена!

Ответы [ 5 ]

1 голос
/ 15 апреля 2011
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();
1 голос
/ 15 апреля 2011

Это было не так тривиально, как я думал. Вероятно, наиболее разумным решением (кроме рекурсивного построения списка) является реализация компаратора для этого, чтобы выполнить сортировку.

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

(Видя, что ответ Стека показывает, что я был почти там с тем, что у меня было изначально. Просто мне нужно было использовать ординальный компаратор строк. Так что получается, что он работает с этим изменением.)

С другой стороны, мы могли бы использовать некоторые свойства структуры каталогов, чтобы упростить эту задачу и не реализовывать компаратор.

var query = Directory
    .EnumerateDirectories(@"D:\Project\uploads", "*", SearchOption.AllDirectories)
    .OrderBy(name => name.Replace(Path.DirectorySeparatorChar, '\0'), StringComparer.Ordinal);
0 голосов
/ 15 апреля 2011

Спасибо за ваши комментарии и ответы, ребята,

Я думаю, что жизнь с рекурсивом будет намного проще

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();
}
0 голосов
/ 15 апреля 2011

Единственное, что вам нужно изменить в порядке по умолчанию, это убедиться, что символ \ всегда рассматривается как первая буква в вашем алфавите.У меня нет точного ответа, как это реализовать, но:

  • Вы можете использовать предложение order by, если найдете способ заменить \ в строке на символон меньше всех других символов и использует эту замененную строку в качестве ключа.

  • Вы можете использовать Array.Sort и реализовать свой компаратор строк, который повторно реализует сравнение строк, но кодирует этот дополнительныйправило о символе \.

0 голосов
/ 15 апреля 2011

С .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

, чтобы получить еще больший контроль / гибкость.Выберите самый простой подход в зависимости от ваших потребностей

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...