linq Group By 并获取 first 和 lats 记录

linq Group By and take first and lats records

我正在处理对我来说可能简单但困难的问题。 我正在为停放的汽车拍照,并通过车牌保存它们并加盖时间戳。同一辆车一天可以拍好几次

示例数据

Plate Brand Model InsertDate
99AA111 Tesla S 2022-01-17 04:00:00
99AA111 Tesla S 2022-01-17 04:30:00
99AA111 Tesla S 2022-01-17 05:00:00
59TA3312 Nissan Skyline 2022-01-17 04:00:00
59TA3312 Nissan Skyline 2022-01-17 04:30:00
129EA512 Subaru Impreza 2022-01-17 03:30:00

我想要实现的是;

Plate Brand Model FirstPhotoDate SecondPhotoDate
99AA111 Tesla S 2022-01-17 04:00:00 2022-01-17 04:30:00
99AA111 Tesla S 2022-01-17 05:00:00 -
59TA3312 Nissan Skyline 2022-01-17 04:00:00 2022-01-17 04:30:00
129EA512 Subaru Impreza 2022-01-17 03:30:00 -

我想到了;

var groupedResult = resultList.GroupBy(f => f.Plate).Select(f => new ResultListView
{
    Plate = f.Key,
    FirstDate = f.Min(f => f.InsertDate),
    SecondDate = f.Max(f => f.InsertDate),
    Brand = f.FirstOrDefault().Brand,
    Model = f.FirstOrDefault().Model,
    TimeDifference = (f.Max(f => f.InsertDate) - f.Min(f => f.InsertDate)).TotalMinutes
});

但是在代码中很明显,它只给了我第一条和最后一条记录,而不是我预期的那样。我正在尝试按盘子分组,如果同一个盘子不止一次,请将其与下一个盘子匹配。如果只有一张照片或三张照片,则第二个日期应为空。

当然可以应用不同的逻辑,但我相信这是更清晰的方法。

我认为在分组结果中循环。取第一个和第二个并跳过两个。但这不是我要找的。

感谢您的帮助。

嗯,有(让我们使用命名元组来演示):

var resultList = new (string Plate, string Brand, string Model, DateTime InsertDate)[] {
  ("99AA111",  "Tesla",  "S",       new DateTime(2022,01,17, 04,00,00)),
  ("99AA111",  "Tesla",  "S",       new DateTime(2022,01,17, 04,30,00)),
  ("99AA111",  "Tesla",  "S",       new DateTime(2022,01,17, 05,00,00)),
  ("59TA3312", "Nissan", "Skyline", new DateTime(2022,01,17, 04,00,00)),
  ("59TA3312", "Nissan", "Skyline", new DateTime(2022,01,17, 04,30,00)),
  ("129EA512", "Subaru", "Impreza", new DateTime(2022,01,17, 03,30,00)),
};

你可以GroupBy两次:

var groupedResult = resultList
  .GroupBy(item => item.Plate)
  .SelectMany(group => group
     .OrderBy(item => item.InsertDate)
     .Select((item, index) => (item: item, index: index / 2))
     .GroupBy(pair => pair.index)
     .Select(g => (
        Plate: g.First().item.Plate,
        Brand: g.First().item.Brand,
        Model: g.First().item.Model,
        FirstPhotoDate: g.First().item.InsertDate,
        SecondPhotoDate: (g.Count() == 1 ? null : (DateTime?)(g.Last().item.InsertDate))
     ))
   );

一起来看看:

string report = string.Join(Environment.NewLine, groupedResult
  .Select(r => $"{r.Plate,-8} : {r.Brand,-6} : {r.Model,-7} : {r.FirstPhotoDate} : {r.SecondPhotoDate}"));

Console.Write(report);

结果:

99AA111  : Tesla  : S       : 17.01.2022 4:00:00 : 17.01.2022 4:30:00
99AA111  : Tesla  : S       : 17.01.2022 5:00:00 : 
59TA3312 : Nissan : Skyline : 17.01.2022 4:00:00 : 17.01.2022 4:30:00
129EA512 : Subaru : Impreza : 17.01.2022 3:30:00 : 

这是我能想到的最干净的方法:

var resultList = new (string Plate, string Brand, string Model, DateTime InsertDate)[]
{
    ("99AA111",  "Tesla",  "S",       new DateTime(2022,01,17, 04,00,00)),
    ("99AA111",  "Tesla",  "S",       new DateTime(2022,01,17, 04,30,00)),
    ("99AA111",  "Tesla",  "S",       new DateTime(2022,01,17, 05,00,00)),
    ("59TA3312", "Nissan", "Skyline", new DateTime(2022,01,17, 04,00,00)),
    ("59TA3312", "Nissan", "Skyline", new DateTime(2022,01,17, 04,30,00)),
    ("129EA512", "Subaru", "Impreza", new DateTime(2022,01,17, 03,30,00)),
};

var query =
    from r in resultList
    group (DateTime?)r.InsertDate by new { r.Plate, r.Brand, r.Model } into grs
    from bgrs in grs.OrderBy(x => x).Buffer(2)
    select new
    {
        grs.Key.Plate,
        grs.Key.Brand,
        grs.Key.Model,
        FirstPhotoDate = bgrs.First(),
        SecondPhotoDate = bgrs.Skip(1).Any() ? bgrs.Last() : null,
    };

我使用了 Microsoft System.Interactive NuGet 中的 Buffer 运算符。

这给了我: