You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于指定起始小时的LINQ按时间段分组Exam问题求助

按指定起始小时的时间段分组Exam为Visit的LINQ实现

需求说明

需要编写LINQ语句,将Exam按**以一天中指定小时为起始点的时间段(1天或2天)**分组为Visit,要求从单元测试场景出发实现逻辑。

基础测试数据

1st exam with recording date 2024-03-21T11:20:00, Id = 3; 
2nd exam with recording date 2024-03-21T11:50:00, Id = 4; 
3rd exam with recording date 2024-03-21T12:20:00, Id = 5; 
4th exam with recording date 2024-03-21T12:50:00, Id = 6; 
5th exam with recording date 2024-03-22T11:20:00, Id = 7; 
6th exam with recording date 2024-03-22T11:50:00, Id = 8; 
7th exam with recording date 2024-03-22T12:20:00, Id = 9; 
8th exam with recording date 2024-03-23T12:50:00, Id = 10; 
9th exam with recording date 2024-03-23T11:20:00, Id = 11; 
10th exam with recording date 2024-03-23T11:50:00, Id = 12; 
11th exam with recording date 2024-03-23T12:20:00, Id = 13; 
12th exam with recording date 2024-03-23T12:50:00, Id = 24; 

期望分组结果

按中午12点为起始的1天时间段分组

1st visit with Id = 1 and exams with Id = [3, 4]
2nd visit with Id = 5 and exams with Id = [5, 6, 7, 8]
3rd visit with Id = 9 and exams with Id = [9, 10, 11, 12]
4th visit with Id = 13 and exams with Id = [13, 24]

按中午12点为起始的2天时间段分组

1st visit with Id = 1 and exams with Id = [3, 4]
2nd visit with Id = 5 and exams with Id = [5, 6, 7, 8, 9, 10, 11, 12]
3rd visit with Id = 13 and exams with Id = [13, 24]

单元测试代码

[TestMethod]
public void Convert_TwoExamSplitAtNoon_TwoVisits()
{
    PatientWithExams p = new() { PatientId = 1 };

    p.Exams.Add(new LightExam()
    {
        ExamId = 1,
        ExamType = Micromed.Domain.Archive.Types.ExamFileTypeId.EegTrace,
        RecordingDate = new DateTime(2024, 3, 21, 11, 20, 00)
    });

    p.Exams.Add(new LightExam()
    {
        ExamId = 1,
        ExamType = Micromed.Domain.Archive.Types.ExamFileTypeId.EegTrace,
        RecordingDate = new DateTime(2024, 3, 21, 12, 20, 00)
    });

    List<PatientWithExams> patients = new() { p };

    TimeSpan splitTime = new(12, 00, 00);

    Dictionary<int, List<LightVisit>> visits = _converter.ConvertNew(patients, splitTime);

    // Assert
    Assert.AreEqual(true, visits[1].Count() == 2);
}

现有代码(未实现指定小时分割逻辑)

TimeSpan groupInterval = TimeSpan.FromDays(1);

var eegExams = from e in patient.Exams.Where(e => e.ExamType == Domain.Archive.Types.ExamFileTypeId.EegTrace)
               group e by e.RecordingDate.Ticks / groupInterval.Ticks into g
               select new { day = new DateTime(g.Key * groupInterval.Ticks), Values = g.Count(), Exams = g.DefaultIfEmpty() };

解决方案

核心逻辑是先将每个RecordingDate调整为以指定起始小时为基准的分组锚点,再按锚点分组,同时支持自定义分组间隔。

完善后的LINQ代码

// 可作为参数传入的配置项
TimeSpan splitTime = new TimeSpan(12, 0, 0); // 指定起始小时(示例:中午12点)
TimeSpan groupInterval = TimeSpan.FromDays(1); // 分组时长(示例:1天,可改为FromDays(2))

var eegExams = patient.Exams
    .Where(e => e.ExamType == Domain.Archive.Types.ExamFileTypeId.EegTrace)
    .Select(e => new 
    {
        Exam = e,
        // 计算当前Exam所属分组的锚点时间
        GroupAnchor = GetGroupAnchor(e.RecordingDate, splitTime, groupInterval)
    })
    .GroupBy(x => x.GroupAnchor.Ticks)
    .Select(g => new 
    {
        VisitId = g.First().Exam.ExamId, // 按业务需求生成VisitId,示例取分组首个Exam的Id
        Exams = g.Select(x => x.Exam).ToList(),
        GroupStartTime = new DateTime(g.Key)
    });

辅助方法:计算分组锚点

private DateTime GetGroupAnchor(DateTime date, TimeSpan splitTime, TimeSpan groupInterval)
{
    // 计算当天的起始分割点
    DateTime dailySplitPoint = date.Date.Add(splitTime);
    
    // 若当前时间早于分割点,归到前一个分组区间
    DateTime intervalStart = date < dailySplitPoint 
        ? dailySplitPoint.Subtract(groupInterval) 
        : dailySplitPoint;
    
    // 计算该时间所属分组的起始锚点
    long intervalTicks = groupInterval.Ticks;
    long groupKeyTicks = (intervalStart.Ticks / intervalTicks) * intervalTicks;
    
    return new DateTime(groupKeyTicks);
}

代码说明

  • GetGroupAnchor方法处理跨天场景:比如12点前的时间会被归到前一个以12点为起始的分组区间
  • LINQ中先映射每个Exam对应的分组锚点,再按锚点的Ticks值分组,确保同一区间的Exam被分到一组
  • 配置项splitTime和groupInterval可灵活调整,满足不同业务场景的分组需求

内容的提问来源于stack exchange,提问作者Riccardo Zamuner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 15:45:20