<think>我们有一个数组,每个元素包含day, hour, minute等字段。要求按day分组,然后对同一天内的时间点(hour和minute)进行处理:只考虑minute为0的整点,然后按hour的连续性划分时间段(连续的小时数构成一个时间段)。
例如:某一天有[1,2,3,5,6,9]这些整点,那么连续的时间段就是[1-3], [5-6], [9](单独一个点也算一个时间段),所以这一天有3个时间段。
步骤:
1. 按day分组,同时过滤掉minute不为0的数据(因为只考虑整点)。
2. 对同一天的数据,提取hour,并去重(避免同一小时多次出现)和排序。
3. 遍历排序后的hour数组,将连续的小时合并为一个时间段(即相邻两个数差值为1则连续)。
4. 统计每个day的时间段数量,或者记录每个时间段(返回具体的时间段起止)。
但是,用户的问题中并没有要求返回具体的时间段,而是要求每天有几个时间段。不过,为了扩展性,我们可以返回一个对象,包含每天的时间段列表(每个时间段用起止小时表示)以及时间段数量。
根据用户需求,我们可以有两种返回形式:
a) 返回每天的时间段数量(如:{day1: 3, day2: 2})
b) 返回每天的时间段列表(如:{day1: [[1,3],[5,6],[9,9]], day2: [...]})
由于问题描述是“查看每天有几个时间段”,所以主要返回数量。但考虑到后续可能也需要具体时间段,我们可以设计函数返回包含详细信息的对象。
设计函数如下:
输入:数组,元素结构如示例
输出:一个对象,以day为键,值为一个对象,包含:
count: 时间段数量
periods: 时间段列表,每个时间段是一个包含起始小时和结束小时的数组(如果只有一个小时,则起始和结束相同)
注意:day字段在输入中可能是数字(如0,1)或字符串(如"2024-06-01"),但示例中是数字0,1。因此,我们按原样作为键。
步骤:
1. 分组:将同一天的数据放在一起,同时过滤掉minute不为0的项。
2. 对每一天,提取所有hour,去重并排序(升序)。
3. 遍历排序后的hour数组,合并连续的小时。
- 初始化一个空数组用于存放当前连续时间段(currentPeriod),以及一个结果数组(periods)。
- 遍历每个小时,如果当前小时与前一个连续(即当前小时等于上一个连续小时的下一个),则继续当前时间段;否则,将当前时间段结束(保存)并开始新的时间段。
4. 将当前时间段保存到结果数组。
具体实现:
例如:一天的小时数组为 [1,2,3,5,6,9]
初始化:currentStart=1, currentEnd=1
遍历:
i=1: 当前小时2,上一个小时1,连续:currentEnd=2
i=2: 当前小时3,连续:currentEnd=3
i=3: 当前小时5,与3不连续(5-3=2>1),则将[1,3]保存,重置currentStart=5, currentEnd=5
i=4: 当前小时6,连续:currentEnd=6
i=5: 当前小时9,与6不连续,保存[5,6],重置currentStart=9, currentEnd=9
最后保存[9,9]
因此得到三个时间段:[1,3], [5,6], [9,9]
代码实现:</think>根据您的需求,我将编写一个JavaScript函数,按天分组并统计每天的连续时间段数量。以下是实现方案:
### 解决方案代码
```javascript
function groupConsecutiveTimePeriods(data) {
// 1. 按day字段分组,并过滤minute不为0的数据
const groupedByDay = data.reduce((acc, item) => {
if (item.minute !== 0) return acc; // 只处理整点数据
const day = item.day;
if (!acc[day]) acc[day] = new Set(); // 使用Set自动去重
acc[day].add(item.hour);
return acc;
}, {});
// 2. 处理每天的数据,统计连续时间段
const result = {};
for (const day in groupedByDay) {
// 将Set转为数组并排序
const hours = Array.from(groupedByDay[day]).sort((a, b) => a - b);
let count = 0;
let currentStart = null;
// 3. 遍历排序后的小时数组
for (let i = 0; i < hours.length; i++) {
// 如果是第一个小时或与前一个不连续
if (i === 0 || hours[i] !== hours[i - 1] + 1) {
// 结束当前时间段计数
if (currentStart !== null) count++;
// 开始新的时间段
currentStart = hours[i];
}
// 如果是最后一个小时,结束当前时间段
if (i === hours.length - 1 && currentStart !== null) {
count++;
}
}
result[day] = count;
}
return result;
}
```
### 使用示例
```javascript
// 测试数据
const testData = [
{day: 0, hour: 1, minute: 0},
{day: 0, hour: 2, minute: 0},
{day: 0, hour: 3, minute: 0},
{day: 0, hour: 5, minute: 0},
{day: 0, hour: 6, minute: 0},
{day: 0, hour: 9, minute: 0},
{day: 1, hour: 10, minute: 0},
{day: 1, hour: 11, minute: 0},
{day: 1, hour: 12, minute: 0},
{day: 1, hour: 14, minute: 0},
];
// 执行函数
console.log(groupConsecutiveTimePeriods(testData));
/* 输出:
{
"0": 3, // [1-3], [5-6], [9] 共3个时间段
"1": 2 // [10-12], [14] 共2个时间段
}
*/
```
### 关键逻辑说明
1. **数据分组与过滤**:
- 使用`reduce()`按`day`字段分组
- 使用`Set`自动去除重复的小时数据
- 过滤掉`minute !== 0`的非整点数据
2. **连续性检测算法**:
```javascript
for (let i = 0; i < hours.length; i++) {
if (i === 0 || hours[i] !== hours[i - 1] + 1) {
// 开始新时间段
}
// 处理时间段结束
}
```
- 当小时值不连续时(差值不为1),结束当前时间段计数
- 单个整点视为独立时间段
3. **时间复杂度**:
- 分组:$O(n)$
- 排序:$O(m \log m)$(m为每天的小时数)
- 总体:$O(n + m \log m)$
### 扩展功能
如果需要获取具体的时间段范围(而不仅是数量),可修改函数为:
```javascript
function getDetailedTimePeriods(data) {
// ...(同前分组逻辑)
const detailedResult = {};
for (const day in groupedByDay) {
const hours = Array.from(groupedByDay[day]).sort((a, b) => a - b);
const periods = [];
let start = hours[0];
for (let i = 1; i <= hours.length; i++) {
if (i === hours.length || hours[i] !== hours[i - 1] + 1) {
periods.push([start, hours[i - 1]]);
if (i < hours.length) start = hours[i];
}
}
detailedResult[day] = periods;
}
return detailedResult;
}
```
### 相关问题
1. 如何处理跨天的连续时间段(如23点到次日1点)?
**解决方案**:添加日期计算逻辑,使用`Date`对象处理时间连续性[^3]
2. 如果数据中存在分钟非零的干扰项,如何优化过滤逻辑?
**建议**:添加预处理步骤`data.filter(item => item.minute === 0)`
3. 如何扩展此函数以返回具体的连续时间段(如["1:00-3:00"])?
**方案**:修改返回结构,使用模板字符串格式化时间段
```javascript
return `${start}:00-${end}:00`;
```
4. 当数据量极大时(如百万级记录),如何优化性能?
**优化策略**:
- 使用Map代替Object提高分组效率
- 分批处理数据(分页或流式处理)
- 使用Web Workers并行处理[^5]