如何调整代码从List<DataRecord>筛选一小时内记录数最多的记录组?
先说说你的问题核心:原来的代码只按整点小时分组,完全没考虑跨整点的一小时窗口(比如10:50到11:50这种),所以才拿不到预期的3条记录。咱们来一步步解决这个问题。
先明确下需求和现状:
你有一个DataRecord类:
class DataRecord { public double MeasuredValue { get; set; } public DateTime MeasurementDate { get; set; } }
需要找出任意连续一小时区间(不管日期,只看时间范围)里记录数量最多的所有记录。比如你给的示例数据里,10:50-11:50这个区间包含3条记录,是所有可能区间里最多的,所以要返回这3条。
而你现在的代码是按Hour属性分组,相当于把所有11点整到11点59分的记录归为一组,17点的归为一组,这样最多只能拿到2条记录的组,完全没覆盖到跨整点的有效区间。
解决方案:遍历所有可能的一小时窗口
要解决这个问题,我们需要检查每一个可能的一小时时间窗口,统计每个窗口里的记录数,然后找到记录数最多的那个窗口里的所有记录。具体可以这么做:
第一步:先把数据按时间排序
先把所有记录按MeasurementDate从小到大排序,这样后续统计窗口记录的时候可以更高效,不用反复扫全部数据:
var sortedRecords = records.OrderBy(r => r.MeasurementDate).ToList();
第二步:逐个记录作为窗口起点,统计对应一小时内的记录
对每一条记录,我们把它的时间作为窗口的起始点,然后找出所有时间在[起始时间, 起始时间+1小时)范围内的记录,统计数量。同时跟踪记录数最多的窗口:
int maxRecordCount = 0; List<DataRecord> bestWindowRecords = new List<DataRecord>(); for (int i = 0; i < sortedRecords.Count; i++) { var windowStart = sortedRecords[i].MeasurementDate; var windowEnd = windowStart.AddHours(1); // 因为数据已经排序,从当前位置往后取,直到超出一小时窗口 var currentWindowRecords = sortedRecords .Skip(i) .TakeWhile(r => r.MeasurementDate < windowEnd) .ToList(); // 更新最大记录数和对应的窗口记录 if (currentWindowRecords.Count > maxRecordCount) { maxRecordCount = currentWindowRecords.Count; bestWindowRecords = currentWindowRecords; } }
运行这段代码后,bestWindowRecords里就是你要的预期结果——那3条记录。
可选:处理多个窗口记录数相同的情况
如果有多个不同的一小时窗口都有相同的最大记录数,你可能需要把这些窗口的记录都保留下来。这时候可以调整代码,用一个列表来存储所有符合条件的窗口:
int maxRecordCount = 0; List<List<DataRecord>> allBestWindows = new List<List<DataRecord>>(); for (int i = 0; i < sortedRecords.Count; i++) { var windowStart = sortedRecords[i].MeasurementDate; var windowEnd = windowStart.AddHours(1); var currentWindowRecords = sortedRecords .Skip(i) .TakeWhile(r => r.MeasurementDate < windowEnd) .ToList(); if (currentWindowRecords.Count > maxRecordCount) { // 找到更大的记录数,清空之前的结果,添加当前窗口 maxRecordCount = currentWindowRecords.Count; allBestWindows.Clear(); allBestWindows.Add(currentWindowRecords); } else if (currentWindowRecords.Count == maxRecordCount) { // 避免重复添加同一个窗口(比如连续的记录属于同一个窗口) if (!allBestWindows.Any(w => w.First().MeasurementDate == windowStart)) { allBestWindows.Add(currentWindowRecords); } } } // 如果需要把所有最佳窗口的记录合并成一个去重后的列表 var allMaxRecords = allBestWindows.SelectMany(w => w).Distinct().ToList();
为什么这个方法能行?
因为我们没有局限于整点小时的分组,而是考虑了每一条记录对应的一小时窗口,这样就不会漏掉任何可能的、包含最多记录的区间。而且因为数据已经排序,用Skip(i).TakeWhile的方式比每次遍历全部数据要高效不少,尤其是数据量较大的时候。
内容的提问来源于stack exchange,提问作者Erik T.

