The most compact complete solution would be something like:
CultureInfo sloveneCultureInfo = new CultureInfo("sl-SI");
_items = _items.OrderBy(fn => DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", sloveneCultureInfo)).ToList();
The above assigns this list back to itself. If you were going to do so, then you could gain in performance for relatively little increase in code complexity by sorting in-place:
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)));
(If you are running this on a system running in a Slovene locale, then you could use the CurrentCulture
rather than the constructed sloveneCultureInfo
above).
Further improvements can be made by using a comparer class rather than a lambda, and indeed the comparison itself could be more efficient than the above, but I wouldn't worry about this unless the sort proved to be a bottle-neck.
Edit: A break down on just what works here.
There are two convenient ways of sorting a list (strictly, one way of sorting a list and another of sorting any IEnumerable or IQueryable).
OrderBy
takes a parameter that computes a sort key, and returns an IOrderedEnumerable<T>
(or IOrderedQueryable<T>
from now on I'm going to ignore the fact that this can be done on IQueryable
as well, as the principle is the same). This class is one that in most ways acts like an IEnumerable<T>
that is sorted by the key, with the only difference being that if you do a subsequent ThenBy
on it, it keeps ordered by the first key, and hence lets you have multi-stage orderings.
So, in the code:
var x = _items.OrderBy(SomeMethod);
SomeMethod
will return values that can be sorted, and on the basis of this, x will be given an IOrderedEnumerable<T>
sorted accordingly. For example if SomeMethod
took a string and returned the length of the string, then we could use it to sort a list of strings by length.
If we were then going to iterate through the _items, then this is our job done. If we wanted to have a new list then we could call Tolist()
on the results, and that would return such a list.
The other approach, that only works on lists, is to call Sort()
. There is a parameterless form that just sorts according to the default comparison of the type in question (if there is one), a form that takes an IComparer<T>
object (more involved than necessary in most cases, though sometimes very useful) and a form that takes either a delegate or a lambda (strictly it takes a delegate, but we can use a lambda to create that delegate).
This delegate must receive two values of the type of the list's items and return a number that is negative if the first should be sorted earlier than the latter, zero if they are equivalent and a positive number otherwise.
Sort()
changes the list its done on. This has an efficiency gain, but is clearly disastrous if you need to hold onto the original sort order too.
Okay, so far we've got two ways to sort a list. Both of them need a way to turn your file names into something that can be sorted on.
Since the file names relate to dates, and since dates are already sortable, the most sensible approach is to obtain those dates.
We can't create a date directly from the file name, because Avgust 2010 isn't a date, just a month-year value and there isn't a month year class in the BCL (there may be in other libraries but let's not gild lilies).
However, every month has a first day, and therefore we can create a valid date from "01 " concatenated with the file name. So our first step is to say we will act on "01 " + fn
where fn
is the file name.
This gives us strings in the form of e.g. "01 Avgust 2010.csv"
. Examining how date parsing works, we know we can use dd
for the two-digit date, MMMM
for the full name of the month in the relevant language (Slovenian in this case) and yyyy
for the full year. We just need to add on \.\c\s\v
to mean that it will be followed with a ".csv" that we don't parse, and we're set. We can hence turn the file name into the first of its month with DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", new Culture("sl-SI"))
where fn is the file name. To make this a lambda expression we use fn => DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", new Culture("sl-SI"))
.
The first approach is now complete, we call _items.OrderBy(fn => DateTime.ParseExact("01 " + fn, @"dd MMMM yyyy\.\c\s\v", new Culture("sl-SI")))
and depend upon OrderBy
knowing how to sort DateTime
values.
For the second approach we need to take two values and compare them ourselves. We can do this by calling CompareTo()
on the DateTime
s returned by ParseExact
.
Finally, we move the CultureInfo
construction out to initialise a variable called sloveneCultureInfo
to avoid wasteful calls to the multiple creations of essentially the same object.