#linq #sorting #path #hierarchy #flatten
#linq #сортировка #путь #иерархия #сгладить
Вопрос:
Допустим, у меня есть следующие папки:
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:Projectuploads", "*.*", SearchOption.AllDirectories)
select s
Результаты:
D:ProjectuploadsNew Folder
D:ProjectuploadsNew Folder (2)
D:ProjectuploadsNew Folder (3)
D:ProjectuploadsNew Folder (4)
D:ProjectuploadsNew FolderNew Folder
D:ProjectuploadsNew FolderNew Folder (2)
D:ProjectuploadsNew FolderNew Folder (3)
D:ProjectuploadsNew FolderNew Folder (4)
Есть ли способ отсортировать список в правильном порядке? Я ожидал, что это будет:
D:ProjectuploadsNew Folder
D:ProjectuploadsNew FolderNew Folder
D:ProjectuploadsNew FolderNew Folder (2)
D:ProjectuploadsNew FolderNew Folder (3)
D:ProjectuploadsNew FolderNew Folder (4)
D:ProjectuploadsNew Folder (2)
D:ProjectuploadsNew Folder (3)
D:ProjectuploadsNew Folder (4)
Будем признательны за любую помощь!
Ответ №1:
Это было не так тривиально, как я думал. Вероятно, наиболее разумным решением (помимо рекурсивного построения списка) является реализация средства сравнения для выполнения сортировки.
сортировщик каталогов классов: IComparer<строка> { сравнение открытых значений (строка x, строка y) { возвращает StringComparer.Порядковый номер.Сравнить(x.Заменить(Path.DirectorySeparatorChar, ''), y.Заменить(Path.DirectorySeparatorChar, '')); var XPaths = x.Split(Path.DirectorySeparatorChar); var yPaths = y.Split(Path.DirectorySeparatorChar); var minLength = Math.Min(XPaths.Длина, yPaths.Длина); for (int i = 0; i < Минимальная длина; i ) { переменные = XPaths[i].Сравнение(yPaths[i]); if (ires != 0) возвращает ires; } переменные lres = XPaths.Длина.Сравнить (yPaths.Длина); если (lres == 0) { возвращает lres; } иначе, если (lres < 0) { var i = y.lastIndexOf(Path.DirectorySeparatorChar); вернуть x.Длина == i ? lres: -lres; } else //if (lres > 0) { var i = x.lastIndexOf(Path.DirectorySeparatorChar); возвращает y.Длина == i ? lres: -lres; } } }
(Просмотр ответа Стека показывает, что я был почти там с тем, что у меня было изначально. Просто мне нужно было использовать средство сравнения порядковых строк. Итак, оказывается, что это работает с использованием этого изменения.)
С другой стороны, мы могли бы использовать некоторые свойства структуры каталогов, чтобы упростить эту задачу и не внедрять средство сравнения.
var query = Directory
.EnumerateDirectories(@"D:Projectuploads", "*", SearchOption.AllDirectories)
.OrderBy(name => name.Replace(Path.DirectorySeparatorChar, ''), StringComparer.Ordinal);
Комментарии:
1. @NVA: Да, я только что заметил это… не тот порядок, который я ожидал для некоторых входных данных. Позвольте мне посмотреть, смогу ли я это настроить.
2. @Byul: Я выяснил, как реализовать такой компаратор. Итак, если вы хотите сделать это путем сортировки, вы можете использовать этот компаратор. p.s., изменить имя? 🙂
Ответ №2:
private class Comparer : IComparer<string>
{
public int Compare(string x, string y)
{
return StringComparer.Ordinal.Compare(x.Replace(Path.DirectorySeparatorChar, ''),
y.Replace(Path.DirectorySeparatorChar, ''));
}
}
и затем
var source = Directory.GetDirectories(@"D:Projectuploads", "*.*", SearchOption.AllDirectories)
var target = source.OrderBy(x => x, new Comparer()).ToArray();
Комментарии:
1. О боже, я понятия не имел, что использование порядкового средства сравнения имеет значение, хотя создание нового средства сравнения здесь на самом деле не обязательно.
2. да. вы правы. x => x.Replace(Path.DirectorySeparatorChar, ») — хороший селектор ключей с помощью StringComparer. Средство сравнения порядковых номеров
Ответ №3:
Единственное, что вам нужно изменить в порядке по умолчанию, это убедиться, что
символ всегда обрабатывается как первая буква в вашем алфавите. У меня нет точного ответа, как это реализовать, но:
-
Вы можете использовать
order by
предложение, если найдете способ заменить -
Вы можете использовать
Array.Sort
и реализовать свой инструмент сравнения строк, который повторно реализует сравнение строк, но кодирует это дополнительное правило о
Комментарии:
1. Вы также можете взломать свой locale cultureinfo для этой цели. Я бы рекомендовал «говорить, что вы имеете в виду» (преднамеренное программирование)
Ответ №4:
С .NET 4.0 попробуйте
Directory.EnumerateDirectories(@"D:Projectuploads", "*.*", SearchOption.AllDirectories)
это может сделать то, что вы ожидаете. Если этого не происходит, вы можете сделать это явно:
Directory.GetDirectories(@"D:Projectuploads")
.SelectMany(dir => dir.GetDirectories().OrderBy(sub => sub.Name))
Наконец, вы могли бы сделать что-то вроде:
from s in Directory.GetDirectories(@"D:Projectuploads", "*.*", SearchOption.AllDirectories)
order by s.Parent.Name, s.Name
select s
from s in Directory.GetDirectories(@"D:Projectuploads", "*.*", SearchOption.AllDirectories)
let members = s.Name.Split(new [] {Path.SeparatorChar})
order by members[2], s.Name
select s
чтобы получить еще больше контроля / гибкости. Выбрал самый простой подход в зависимости от ваших потребностей
Комментарии:
1. Хм, об этом не подумал.
GetDirectories()
Гарантирует ли возврат каталогов в отсортированном порядке? В противном случае вам пришлось бы сортировать его.2. Я думаю, что вторая версия должна быть рекурсивной.
3. @Tomas: Я могу «тоже так думать», но это нигде не указано явно, позволяя OP решать
Ответ №5:
Спасибо за ваш комментарий и ответ, ребята,
Я думаю, что жизнь будет намного проще с рекурсивным
void Main()
{
string rootFolder = @"D:Projectuploads";
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();
}