C # пользовательская сортировка списка - PullRequest
3 голосов
/ 04 октября 2010

У меня есть программа, которая находит все файлы отчетов .csv в каталоге. Эти отчеты хранятся в списке

List<string> _items = new List<string>();

эти отчеты названы так:

"month year.csv" (пример: "Август" 2010.csv ")

Я бы хотел отсортировать отчеты по месяцам.

Месяцы в этом порядке:

январь, февраль, марек, апрель, майор, июнь, июль, август, сентябрь, октябрь, ноябрь, декабрь

Как я могу это сделать?

Br, Wolfy

Ответы [ 2 ]

4 голосов
/ 04 октября 2010

Похоже, вы должны написать метод для разбора имени файла в DateTime. Например, он может снять расширение, разделить остаток на пробел, а затем проанализировать вторую часть как год и найти название месяца в таблице.

Получив этот метод, вы можете просто сделать:

_items = _items.OrderBy(FilenameToDate).ToList();
3 голосов
/ 04 октября 2010

Самое компактное законченное решение будет выглядеть примерно так:

CultureInfo sloveneCultureInfo = new CultureInfo("sl-SI");
_items = _items.OrderBy(fn => DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", sloveneCultureInfo)).ToList();

Вышесказанное присваивает этот список себе. Если вы собираетесь это сделать, то вы можете получить прирост производительности при относительно небольшом увеличении сложности кода путем сортировки на месте:

CultureInfo sloveneCultureInfo = new CultureInfo("sl-SI");
_items.Sort((x, y) => DateTime.ParseExact("01 " + x, @"dd MMMM yyyy\.\c\s\v",sloveneCultureInfo).CompareTo(DateTime.ParseExact("01 " + y, @"dd MMMM yyyy\.\c\s\v", sloveneCultureInfo)));

(Если вы работаете в системе, работающей в словенском языке, вы можете использовать CurrentCulture вместо созданного sloveneCultureInfo выше).

Дальнейшие улучшения могут быть сделаны с помощью класса сравнения, а не лямбды, и само сравнение может быть более эффективным, чем приведенное выше, но я не стал бы беспокоиться об этом, если бы сортировка не оказалась узким местом.

Редактировать: разбить только на то, что работает здесь.

Существует два удобных способа сортировки списка (строго один способ сортировки списка и другой способ сортировки любого IEnumerable или IQueryable).

OrderBy принимает параметр, который вычисляет ключ сортировки, и возвращает IOrderedEnumerable<T> (или IOrderedQueryable<T> с этого момента. Я собираюсь игнорировать тот факт, что это можно сделать и на IQueryable, так как принцип тот же). Этот класс в большинстве случаев действует как IEnumerable<T>, отсортированный по ключу, с той лишь разницей, что если вы выполните последующую ThenBy для него, он будет упорядочен по первому ключу и, следовательно, позволит вам есть многоэтапные заказы.

Итак, в коде:

var x = _items.OrderBy(SomeMethod);

SomeMethod вернет значения, которые могут быть отсортированы, и на основании этого x будет дано IOrderedEnumerable<T>, отсортированных соответственно. Например, если SomeMethod взял строку и вернул длину строки, то мы могли бы использовать ее для сортировки списка строк по длине.

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

Другой подход, который работает только со списками, заключается в вызове Sort(). Существует форма без параметров, которая просто сортируется в соответствии со сравнением по умолчанию рассматриваемого типа (если он есть), форма, которая принимает объект IComparer<T> (в большинстве случаев более сложный, чем необходимый, хотя иногда очень полезный) и форма, которая принимает либо делегат, либо лямбду (строго, это делегат, но мы можем использовать лямбду для создания этого делегата).

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

Sort() меняет список, в котором он сделан. Это дает выигрыш в эффективности, но явно губительно, если вам нужно придерживаться исходного порядка сортировки.

Хорошо, пока у нас есть два способа сортировки списка. Им обоим нужен способ превратить ваши имена файлов во что-то, что можно отсортировать.

Поскольку имена файлов относятся к датам, а даты уже сортируются, наиболее разумным является получение этих дат.

Мы не можем создать дату непосредственно из имени файла, потому что Avgust 2010 - это не дата, а значение месяца-года, и в BCL нет класса месяца-года ( могут быть в других библиотеках, но давайте не будем позолотить лилии).

Однако у каждого месяца есть первый день, и поэтому мы можем создать действительную дату из «01», объединенную с именем файла. Итак, наш первый шаг - сказать, что мы будем действовать на "01 " + fn, где fn - имя файла.

Это дает нам строки в форме, например, "01 Avgust 2010.csv". Изучая, как работает разбор дат, мы знаем, что мы можем использовать dd для двузначной даты, MMMM для полного названия месяца на соответствующем языке (в данном случае словенском) и yyyy для полного года. Нам просто нужно добавить \.\c\s\v, чтобы означать, что за ним последует ".csv", который мы не анализируем, и мы настроены. Следовательно, мы можем превратить имя файла в первое число месяца с DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", new Culture("sl-SI")), где fn - имя файла. Чтобы сделать это лямбда-выражение, мы используем fn => DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", new Culture("sl-SI")).

Первый подход завершен, мы называем _items.OrderBy(fn => DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", new Culture("sl-SI"))) и зависим от OrderBy, зная, как сортировать DateTime значения.

Для второго подхода нам нужно взять два значения и сравнить их самостоятельно. Мы можем сделать это, вызвав CompareTo() на DateTime s, возвращаемых ParseExact.

Наконец, мы перемещаем конструкцию CultureInfo, чтобы инициализировать переменную с именем sloveneCultureInfo, чтобы избежать расточительных вызовов нескольких созданий по существу одного и того же объекта.

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