简体   繁体   中英

Sort part of a list in descending order (by date), the other part in ascending order (alphabetically)?

I have a control which shows a list of all PDFs in a selected folder. The names of most of these PDFs (for example, meeting minutes) begin with a date. I want these PDFs shown in descending order so that the most recent PDFs are shown at the top.

In the same folder I also have some PDFs whose names do not contain a date (for example, policies). I want these files to be ordered alphabetically.

Here is an example of the kind of list I have and how I want it to be ordered:

  • 2019-01-12 Meeting minutes.pdf
  • 2018-11-19 Meeting agenda.pdf
  • 2018-06-02 Meeting minutes.pdf
  • 2017-12-13 Meeting agenda.pdf
  • 2017-04-27 Meeting minutes.pdf
  • Privacy policy.pdf
  • Safeguarding policy.pdf
  • Welfare policy.pdf

And here is what I have tried:

private void GenerateFolder()
{
    folder_path = Server.MapPath(BaseFolder);
    _folder_view = new StringBuilder();

    if (Directory.Exists(folder_path))
    {
        DirectoryInfo info = new DirectoryInfo(folder_path);
        FileInfo[] files = info.GetFiles().OrderByDescending(p => p.FullName).ToArray();

        foreach (FileInfo file in files)
        { 
            doStuff();
        }
    }

    folder_view.Text = _folder_view.ToString();
}

With this code, the PDFs whose names begin with a date are ordered in descending order (by date) which is what I want. But the other PDFs whose names do not begin with a date are not ordered the way I would like (alphabetically). Is there a way to accomplish my dual-sorting objective?

I would create a custom class that parses out the date and the rest of the filename in to separate properties, you can then use OrderByDescending and ThenBy to sort on these individual properties.

public class ParsedFilename
{
    public ParsedFilename(string filename)
    {
        FullName = filename;
        if (filename.Length >= 12 &&
            DateTime.TryParse(filename.Substring(0, 10), out var date))
        {
            Date = date;
            Name = filename.Substring(11);
        }
        else
        {
            Date = null;
            Name = filename;
        }
    }

    public DateTime? Date { get; }
    public string Name { get; }
    public string FullName { get; }
}

You can use it like this:

var data = new List<string>(new[]
    {
        "2019-01-12 Meeting minutes.pdf",
        "Safeguarding policy.pdf",
        "2017-04-27 Meeting minutes.pdf",
        "2018-06-02 Meeting minutes.pdf",
        "2017-12-13 Meeting agenda.pdf",
        "Privacy policy.pdf",
        "Welfare policy.pdf",
        "2018-11-19 Meeting agenda.pdf"
    });

var parsedData = data.Select(d => new ParsedFilename(d));

var sortedData = parsedData.OrderByDescending(d => d.Date)
                           .ThenBy(d => d.Name);

var output = sortedData.Select(d => d.FullName);

It produces the following output:

2019-01-12 Meeting minutes.pdf
2018-11-19 Meeting agenda.pdf
2018-06-02 Meeting minutes.pdf
2017-12-13 Meeting agenda.pdf
2017-04-27 Meeting minutes.pdf
Privacy policy.pdf
Safeguarding policy.pdf
Welfare policy.pdf

Depending on the formats of the filenames in your directory, you may have to add some more robust parsing.

In a comment, LocEngineer suggested (paraphrasing):

Start by using a regex-based technique as shown by answers to this question to determine those file names that begin with dates. And then generate two partial lists which you would then combine.

In the spirit of that suggestion, here's a demonstration of one possible solution:

public void CustomFileNameOrderingDemo()
{
    var files = new[]
    {
        "2017-04-27 Meeting minutes.pdf",
        "2018-11-19 Meeting agenda.pdf",
        "2019-01-12 Meeting minutes.pdf",
        "2018-06-02 Meeting minutes.pdf",
        "2017-12-13 Meeting agenda.pdf",
        "Safeguarding policy.pdf",
        "Welfare policy.pdf",
        "Privacy policy.pdf",
    };

    var filesWithDates = FindFilesWithDates().OrderByDescending(f => f).ToList();
    var filesWithoutDates = files.Except(filesWithDates).OrderBy(f => f);
    var result = filesWithDates.Concat(filesWithoutDates);

    IEnumerable<string> FindFilesWithDates()
    {
        return files.Where(f => Regex.IsMatch(f, @"^[0-9]{4}-[0-9]{2}-[0-9]{2} "));
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM