获取最长日期为

时间:2018-01-03 16:12:43

标签: c# linq lambda

我有一个文件列表:

fileA_20180103110932
fileA_20180103111001
fileB_20180103110901
fileC_20180103110932
fileC_20180103111502

每个文件名,我需要获取最新日期。所以结果集将是:

fileA_20180103111001
fileB_20180103110901
fileC_20180103111502

我如何使用lambda表达式?

在高级别上,我认为我必须按文件名分组(所以要做一个子串直到下划线),然后得到那些有一个计数>的文件名的最大日期。 2。

2 个答案:

答案 0 :(得分:2)

这样的事情应该有效:

var files = new List<string>
{
    "fileA_20180103110932",
    "fileA_20180103111001",
    "fileB_20180103110901",
    "fileC_20180103110932",
    "fileC_20180103111502"
};

var results = files
    .Select(f => f.Split('_'))
    .GroupBy(p => p[0], p => p[1])
    .Select(g => g.Key + "_" + g.Max());

答案 1 :(得分:0)

显然,您的文件名中只有一个下划线。将下划线后面的部件定义为“文件日期”与您的问题无关。相关的是你的文件名有一个下划线,一个在下划线之前,一个在下划线之后。

此外,文件名不是文件,它只是一个有一些限制的字符串,尤其是你的编码文件名

所以你的问题就是这样:

给定一系列字符串,其中每个字符串只有一个下划线。下划线之前的部分称为MainPart,下划线之后的部分称为SortablePart(这就是您所谓的“文件日期”)。

您的要求是:

  

我想要一个linq语句,其中包含这个字符串序列和输入   作为输出包含输入的MainPart的字符串序列   字符串,后跟下划线,后跟all的第一个值   具有相同MainPart的字符串的SortableParts以降序排序   顺序。

重新解决你的问题你的linq语句相当容易。您需要一个函数将输入字符串拆分为MainPart和SortablePart。我将使用String.Split

执行此操作
var result = fileNames
    .Select(inputString => inputString.Split(new char[] {'_'}))
    .Select(splitStringArray => new
    {
        MainPart = splitStringArray[0],
        SortablePart = splitStringArray[1],
    })
    // now easy to group by MainPart:
    .GroupBy(
        item => item.MainPart,        // make groups with same MainPart, will be the key
        item => item.SortablePart)    // the elements of the group
     // for every group, sort the elements descending and take only the first element
     .Select(group => new
     {
         MainPart = group.Key,
         NewestElement = group                               // take all group elements
            .SortByDescending(groupElement => groupElement)  // sort in descending order
            .First(),
      })
      // I know every group has at least one element, otherwise it wouldn't be a group
      // now form the file name:
      .Select(item => item.MainPart + '_' + item.NewestElement);
  

这是一个可怕的linq声明!

此外,如果您的文件名根本没有下划线,它将崩溃。很难保证文件名都被正确编码。

如果您的编码文件名是您在应用程序中广泛使用的内容,我的建议是为此创建一个类以及一些函数来转换为filename(字符串)并返回更容易。这将使您的编码文件名更容易被其他人理解,如果需要更容易更改,最重要的是:您可以确定文件名是否正确编码

class CodedFileName
{
    private const char separator = '_';
    public string MainPart {get; private set;}
    public string TimePart {get; private set;}
}

如果您决定更改分隔符或接受多个分隔符(使用下划线的旧文件名和使用减号的新文件名),这会更容易

你还需要一个属性构造函数:

public CodedFileName(string mainPart, DateTime fileDate) {...}

也许构造函数采用文件名。如果没有编码,则为例外:

public CodedFileName(string fileName) {..}
public CodedFileName(FileInfo fileInfo) {...}

public bool IsProperlyCoded(string fileName) {...}

当然是ToString():

public override ToString()
{
    return this.MainPart + separator + this.TimePart;
}

TODO:如果需要,可以考虑定义相等,IEquatable,IComparable,ICloneable等。

完成此操作后,优点是您可以确定您的文件名将始终正确编码。更容易理解他人,更容易改变,从而维护,最后你的linq查询将更容易(理解,维护,测试等):

作为扩展功能:see Extension methods demystified

static class CodedFileNameExtensions
{
    public static CodedFileName Newest(this IEnumerable<CodedFileName> source)
    {
        // TODO: exception if null or source empty
        return source.OrderByDescending(sourceElement => sourceElement.TimePart)
             .First();
    }

    public static CodedFileName NewestOrDefault(this IEnumerable<CodedFileName> source)
    {
         // TODO: exception if null source
         if (source.Any())
            return source.Newest();
         else
            return null;
    }

    public static IEnumerable<CodedFileName> ExtractNewest(this IEnumerable<CodedFileName> source)
    {
        return groupsSameNamePart = source
            .GroupBy(sourceElement => sourceElement.MainPart)
            .Newest(group => group)
    }
}

用法将是:

IEnumerable<string> fileNames = ...
IEnumerable<string> correctlyCodedFileNames = fileNames
    .Where(fileName => fileName.IsCorrectlyCoded();

IEnumerable<CodedFileName> codedFileNames = correctlyCodedFileNames
   .Select(correctlyCodedFileName => new CodedFileName(correctlyCodedFileName));
IEnumerable<CodedFileName> newestFiles = codedFileNames.ExtractNewest();

或者在一个声明中:

IEnumerable<CodedFileName> newestFiles = fileNames
    .Where(fileName => fileName.IsCorrectlyCoded)
    .Select(fileName => new CodedFileName(fileName)
    .ExtractNewest();

现在不是那么容易理解吗?所有这一切都只需要一页编码。

因此,如果您在整个项目中使用编码文件名,我的建议是考虑为其创建一个类。