All, I have written some code to get the latest files from a directory, the files I am looking for contain the string sProcScript = "sProcScript" and in the selected directory I might have several versions:

sProcScriptBigGuns1001.sql
sProcScriptBigGuns1007.sql
sProcScriptEdAggVal1006.sql
sProcScriptEdAggVal1007.sql
...

and I want to pick up the latest version only (the version number being the number after the file name i.e. 1007 etc.). So in the above this would be sProcScriptBigGuns1007.sql and sProcScriptEdAggVal1007.sql. To do this I wrote the following snippet (as part of a larger method), where strPath is the path to the relevant directory

DirectoryInfo dirInfo = new DirectoryInfo(strPath);
List<FileInfo> listFileInfo = new List<FileInfo>();
listFileInfo = dirInfo.EnumerateFiles().ToList<FileInfo>();

// Loop in to locate special processes.
List<FileInfo> specialProcList = new List<FileInfo>();
foreach (FileInfo fi in listFileInfo)
    if (Path.GetFileNameWithoutExtension(fi.FullName).Contains(sProcScript))
        specialProcList.Add(fi);

// Get distinct.
List<string> nameList = new List<string>();
foreach (FileInfo fi in specialProcList)
{
    string strsProc = 
        Path.GetFileNameWithoutExtension(Regex.Replace(fi.Name, @"[\d]", String.Empty));
    if (!nameList.Contains(strsProc))
        nameList.Add(strsProc);
}
List<FileInfo> tmpList;
List<string> tmpNameList = new List<string>();
tmpList = specialProcList.OrderByDescending(f => f.Name).ToList();
foreach (string name in nameList)
{
    foreach (FileInfo fi in tmpList)
    {
        if (fi.Name.Contains(name))
        {
            tmpNameList.Add(fi.Name);
            listFilesFound.Add(fi);
            break;
        }
    }
}

which works great. However, I think there will be a much better and neater way of doing this using LINQ but I am new to LINQ and can't seem to get started with it (the attempts I have made are poor enough to not want to post them here!). Can someone demonstate a better way of doing the above operation using LINQ?

Thanks for your time.

migrated from stackoverflow.com Jul 10 '12 at 14:14

This question came from our site for professional and enthusiast programmers.

  • This is not a code review question. I have demonstrated a viable solution to illustrate I don't want the work done for me. The question could be answered without the code snippet. – Killercam Jul 10 '12 at 14:12
  • This is part of even larger method? I think writing smaller methods would make your code more readable. – svick Jul 10 '12 at 15:04
up vote 2 down vote accepted

Another one (assuming the filename is always SomeCharactersSomeNumbers.extension):

void Main()
{
    var sProcScript = "sProcScript";

    var fileNames = new [] {"sProcScriptBigGuns1001.sql", 
                            "sProcScriptBigGuns1007.sql",
                            "sProcScriptEdAggVal1006.sql",
                            "sProcScriptEdAggVal1007.sql"};

    var grouped = from f in fileNames
                  where f.Contains(sProcScript)
                  let m = new {FileName = f, 
                               Group = new string(f.TakeWhile(c=>!Char.IsDigit(c)).ToArray()),
                               Version = Int32.Parse(new string(f.Where(c=>Char.IsDigit(c)).ToArray()))}
                  group m by m.Group;

    var files = grouped.Select(g => g.OrderByDescending(f => f.Version).First().FileName);                    

    foreach (var file in files)
        Console.WriteLine(file);

}

This should do it:

var selectedFileInfos = listFileInfo
    .Where(x => { 
        var filename = Path.GetFileNameWithoutExtension(x.FullName); 
        return specialProcList.Any(y => filename.Contains(y);})
    .OrderByDescending(x => x.Name)
    .GroupBy(x => Path.GetFileNameWithoutExtension(Regex.Replace(x.Name, @"[\d]", String.Empty)) 
    .Select(x => x.First());

But be careful: file numbers should be padded with zeros. 0001, 0900, 0999, 1000, 1001. If they are just normally numbered with 1, 900, 999, 1000 etc., even your code will fail.

  • Thanks very much, however the edit to you answer was required to get it to work. The edit compiles but returns nothing - can you advise. Again, thanks very much for your time... – MoonKnight Jul 10 '12 at 14:30

Try:

Directory.EnumerateFiles(filePath)
    .Select(f => new { FileName = f, SprocName = regex.Replace(Path.GetFileNameWithoutExtension(f), ""), Version = int.Parse(regex.Match(f).Value) })
    .Where(f => f.FileName.Contains(sProcScript))
    .GroupBy(f => f.SprocName)
    .Select(g => new { Sproc = g.Key, MaxVersion = g.Max(s => s.Version) })
    .Dump();

How about:

var files = from file in new DirectoryInfo(strPath).EnumerateFiles()
            let nameWithoutEx = Path.GetFileNameWithoutExtension(file.Name)
            where nameWithoutEx.Contains(sProcScript)
            let key = new string(nameWithoutEx.Reverse()
                                              .SkipWhile(char.IsDigit)
                                              .Reverse()
                                              .ToArray())
            group file by key into versionGroup
            select versionGroup
                  .OrderBy(f => int.Parse(new string(f.Name
                                                      .Skip(versionGroup.Key.Length)
                                                      .TakeWhile(char.IsDigit)
                                                      .ToArray())))
                  .Last();
  • Thanks but as this stands it won't compile due to a number of errors. Thanks for your time... – MoonKnight Jul 10 '12 at 14:43
  • @Killercam: Didn't try, but what are the errors? What version of C# / .NET are you on? – Ani Jul 10 '12 at 14:45
  • Framework 4.0, there were a few. I have an answer above working so I wont waste anymore of your time. But thanks you very much for your response it is most appreciated. – MoonKnight Jul 10 '12 at 14:50
  • @Killercam: The only thing missing was the ToArray() calls. Fine now. – Ani Jul 10 '12 at 14:59

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.