1

I'm trying to find a decent way to sort an array of strings which contains dates by dates descending.

For instance my list contains this :

page1.aspx_12-05-2013.aspx
page2.aspx_12-04-2010.aspx
page1.aspx_17-09-2014.aspx
page1.aspx_11-01-2013.aspx

I already managed to sort the dates in the strings but i don't know how to do it by keeping the text before This is how i do to sort the dates:

    List<FileInfo> fi = new List<FileInfo>();
    fi = System.IO.Directory.GetFiles(@"C:\Users\arn\Desktop\testlatestpr")
            .Select(x => new System.IO.FileInfo(x))
            .ToList();

    List<string> listofdates = new List<string>();
    foreach (var x in fi)
    {
        Console.WriteLine(x.Name.Substring(x.Name.Length - 15, 10).ToString());
        listofdates.Add(x.Name.Substring(x.Name.Length - 15, 10).ToString());
    }
    var orderedList = listofdates.OrderByDescending(x => DateTime.Parse(x)).ToList();

Thanks for your help

1
  • You know, if you have the date part in ISO format (yyyy-mm-dd) the sort order will be correct if sorted by normal string sorting - which means you could avoid the parsing of each date (you'd still need the substring bit). If there are a LOT of dates, it could be noticably faster. Commented Oct 14, 2014 at 19:46

4 Answers 4

6

Just move your statement to OrderBy:

fi.OrderByDescending(f => DateTime.Parse(f.Name.Substring(x.Name.Length - 15, 10)));
Sign up to request clarification or add additional context in comments.

2 Comments

Mmmm performance wise this wouldn't be optimal would it? If lists are big and performance is an issue I would first separate the date from the complete string in a list of objects containing both the date and the string, then sort that list by the date property of each object. Something like that.
@SaintJob2.0 This parses the date exactly once par item, so it's optimal.
2

You can expand your lambda:

var orderedList = listOfItems
    .OrderByDescending(item => {
        if (item.Name.Length < 15)
            return DateTime.MinValue;

        var datePart = item.Name.Substring(item.Name.Length - 15, 10);
        DateTime date;
        if (!DateTime.TryParse(datePart, out date))
            return DateTime.MinValue;

        return date;
    })
    .ToList();

I've added some checks so it doesn't throw badly if it gets unexpected data.

Comments

1

I would probably do something like this:

Regex rxDate = new Regex( @"(?<day>\d+)-(?<month>\d+)-(?<year>\d+)\..*$" ) ;

string[] unsorted = { "page1.aspx_12-05-2013.aspx" ,
                      "page2.aspx_12-04-2010.aspx" ,
                      "page1.aspx_17-09-2014.aspx" ,
                      "page1.aspx_11-01-2013.aspx" ,
                    } ;

string[] sorted = unsorted
                  .Select( s => {
                    Match m     = rxDate.Match(s) ;
                    int   day   = m.Success ? int.Parse(m.Groups[ "day"   ].Value) : 0 ;
                    int   month = m.Success ? int.Parse(m.Groups[ "month" ].Value) : 0 ;
                    int   year  = m.Success ? int.Parse(m.Groups[ "year"  ].Value) : 0 ;
                    return new { Year=year , Month = month , Day = day , Name = s } ;
                  })
                  .OrderByDescending( x => x.Year )
                  .ThenByDescending( x => x.Month )
                  .ThenByDescending( x => x.Day   )
                  .Select( x => x.Name )
                  .ToArray()
                  ;

This has the advantage that you don't have to worry about filenames tagged with with invalid dates and other data issues (which, sooner or later, you will. DAMHIKT.) The regular expression guarantees you've got things that can be turned into integers and you let LINQ do its sorty thing.

If you want to sort invalid filenames high rather than low, change the default values from 0 to something like int.MaxValue.

1 Comment

The OP had orderedList as List<DateTime> instead of a String array.
0

You can do the Substring inline.

List<string> listofdates = new List<string>();
foreach (var x in fi)
{
    Console.WriteLine(x.Name.Substring(x.Name.Length - 15, 10));
    listofdates.Add(x);
}
var orderedList = listofdates.OrderByDescending(x => DateTime.Parse(x.Name.Substring(x.Name.Length - 15, 10))).ToList();

One other note. I see you are doing a ToString() on your Substring(). That is not necessary, the return value of Substring() is a string.

 Console.WriteLine(x.Name.Substring(x.Name.Length - 15, 10).ToString());
                                                           ^^^^^^^^^^^

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.