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

从JSON对象数组提取符合特定条件周期的JavaScript算法需求

嘿,针对你要开发的这个从JSON数组里提取特定周期的算法,我整理了一套实用的实现思路和代码参考,咱们来一步步拆解:

先明确需求细节

先再把需求捋清楚,避免理解偏差:

待处理数据:JSON对象数组,每个对象包含year(年份)、month(月份)、colval(仅可取0/1/2/3),时间范围覆盖1960年1月至2016年12月
有效周期判定规则:

  1. 周期内最多仅存在1个colval=0的条目(出现2个及以上0则直接无效)
  2. 周期持续时长至少6个月
  3. 周期内其余所有条目的colval必须大于0
核心实现思路

因为数据是按时间序列排列的(如果不是,第一步必须先排序),最适合用**滑动窗口(双指针)**的方式来处理:通过左右两个指针维护一个动态的窗口,跟踪窗口内0的数量和窗口的时间跨度,一旦窗口符合有效周期的条件,就记录下来。这种方法的时间复杂度是O(n),处理1960-2016共684条数据非常高效。

具体实现步骤
  1. 确保数据按时间排序
    不管输入数据是否有序,都先按年份升序、同一年按月份升序排列,这是滑动窗口逻辑成立的前提:

    const sortedData = [...data].sort((a, b) => {
      if (a.year !== b.year) return a.year - b.year;
      return a.month - b.month;
    });
    
  2. 初始化滑动窗口变量

    • 左指针left:标记窗口的起始位置
    • zeroCount:统计当前窗口内colval=0的条目数量
    • validPeriods:保存所有符合条件的有效周期
  3. 遍历数组维护窗口
    用右指针right遍历每个元素,动态调整左指针的位置,确保窗口始终满足“0的数量≤1”的条件,同时检查窗口时长是否达标:

    • 遇到colval=0的元素,zeroCount加1
    • 当zeroCount>1时,不断移动左指针,直到窗口内0的数量回到≤1(移动时如果左指针指向的元素是0,要把zeroCount减1)
    • 计算窗口的时间跨度:把年月转换成总月数(比如1960年1月=1960*12+1),用结束总月数减去起始总月数再加1,得到实际持续的月数
    • 如果时长≥6且zeroCount≤1,就把这个窗口作为有效周期保存
完整代码示例(JavaScript)
function findValidPeriods(data) {
  // 前置:确保数据按时间排序
  const sortedData = [...data].sort((a, b) => {
    if (a.year !== b.year) return a.year - b.year;
    return a.month - b.month;
  });

  const validPeriods = [];
  let left = 0;
  let zeroCount = 0;

  for (let right = 0; right < sortedData.length; right++) {
    const currentItem = sortedData[right];

    // 更新当前窗口内的0数量
    if (currentItem.colval === 0) {
      zeroCount++;
    }

    // 窗口内0数量超标时,收缩左边界
    while (zeroCount > 1) {
      const leftItem = sortedData[left];
      if (leftItem.colval === 0) {
        zeroCount--;
      }
      left++;
    }

    // 计算窗口的持续月数
    const startTotalMonths = sortedData[left].year * 12 + sortedData[left].month;
    const endTotalMonths = currentItem.year * 12 + currentItem.month;
    const durationMonths = endTotalMonths - startTotalMonths + 1;

    // 符合条件则记录周期
    if (durationMonths >= 6 && zeroCount <= 1) {
      validPeriods.push({
        start: { year: sortedData[left].year, month: sortedData[left].month },
        end: { year: currentItem.year, month: currentItem.month },
        duration: durationMonths,
        entries: sortedData.slice(left, right + 1)
      });
    }
  }

  // 可选:合并重叠/连续的有效周期(比如保留最长的不重叠周期)
  if (validPeriods.length === 0) return validPeriods;
  const mergedPeriods = [validPeriods[0]];
  for (let i = 1; i < validPeriods.length; i++) {
    const last = mergedPeriods[mergedPeriods.length - 1];
    const current = validPeriods[i];
    // 判断是否需要合并:当前周期起始与上一个周期结束连续或重叠
    const lastEndTotal = last.end.year * 12 + last.end.month;
    const currentStartTotal = current.start.year * 12 + current.start.month;
    if (currentStartTotal <= lastEndTotal + 1) {
      const newEndTotal = Math.max(lastEndTotal, current.end.year * 12 + current.end.month);
      mergedPeriods[mergedPeriods.length - 1] = {
        start: last.start,
        end: { year: Math.floor(newEndTotal / 12), month: newEndTotal % 12 || 12 },
        duration: newEndTotal - (last.start.year * 12 + last.start.month) + 1,
        entries: sortedData.slice(
          sortedData.findIndex(item => item.year === last.start.year && item.month === last.start.month),
          sortedData.findIndex(item => item.year === Math.floor(newEndTotal / 12) && item.month === (newEndTotal % 12 || 12)) + 1
        )
      };
    } else {
      mergedPeriods.push(current);
    }
  }

  return mergedPeriods;
}
注意事项
  • 时间排序的必要性:如果输入数据是乱序的,滑动窗口逻辑完全失效,所以排序步骤不能省略
  • 时长计算的准确性:用总月数计算能完美处理跨年的情况(比如1960年11月到1961年4月,总月数差是5,加1后得到正确的6个月)
  • 周期合并逻辑:上面的代码包含了可选的合并逻辑,如果你需要保留所有符合条件的子周期,可以去掉合并部分直接返回validPeriods
  • 边界情况测试:记得测试刚好有1个0且时长6个月、整个时间范围都是有效周期、只有5个月符合条件等极端场景

内容的提问来源于stack exchange,提问作者Alper Dincer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 04:03:46