侧边栏壁纸
  • 累计撰写 218 篇文章
  • 累计创建 59 个标签
  • 累计收到 5 条评论

从等间隔且有序的序列中截取指定范围的子序列并补全缺失项

barwe
2022-05-09 / 0 评论 / 0 点赞 / 546 阅读 / 1,776 字
温馨提示:
本文最后更新于 2022-05-09,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

在项目中需要从月份统计结果中截取指定范围的子序列用于绘制折线图。

假设统计结果数组为

const series = [
    { name: '2020-04', value: 3 },
    { name: '2020-06': value: 5 },
    { name: '2020-08': value: 4 },
    { name: '2021-02': value: 10 },
    { name: '2021-03': value: 15 },
    { name: '2021-05': value: 6 },
]

数据特点是:

  • 已经按照年份-月份先后顺序排序
  • 部分月份信息未给出,需要补零

要解决的问题是,给定时间范围startend,截取出包含end月份在内的子序列,对缺少的月份补零。

因为原序列是有序的,所以我们只需要确定起始位置和结束位置的索引即可。为了保证数组首尾两个位置能够被统一识别,我们在数组首尾添加两个元素:首部添加的元素一定比start的月份小,尾部添加的元素一定比end的月份大

const exSeries = [
    { name: `${start.getFullYear()}-${start.getMonth() - 1}`, value: 0 },
    ...series,
    { name: `${end.getFullYear()}-${end.getMonth() + 1}`, value: 0 },
]

这样做的好处是,使用findIndex方法查找索引永远不会返回-1

const startIndex = exSeries.findIndex(d => new Date(d.name) >= start)
const endIndex = exSeries.findIndex(d => new Date(d.name) > end)

然后截取出子序列:

const truncatedSeries = series.slice(startIndex, endIndex)

然后对缺失的月份补零:

import _ from 'lodash'

/** 将给定的年份和月份拼凑成 yyyy-mm 的字符串形式 */
const YYYYMM = (y: number, m: number) => {
    const m$1 = m.toString().length === 1 ? `0${m.toString()}` : m
    return `${y}-${m$1}`
}

/** 穷举出指定范围内的所有 yyyy-mm 形式的日期 */
const listMonth = (start: Date, end: Date) => {
    const sy = start.getFullYear()
    const ey = end.getFullYear()
    const sm = start.getMonth()
    const em = end.getMonth()
    // 年份相同看月份
    if (sy === ey) {
        return _.times(em - sm + 1).map(m => YYYYMM(sy, m + sm + 1))
    }
    // 年份差1
    if (ey - sy === 1) {
        return [
            ..._.times(12 - sm).map(m => YYYYMM(sy, m + sm + 1)),
            ..._.times(em + 1).map(m => YYYYMM(ey, m + 1)),
        ]
    }
    // 年份差2及以上
    if (ey - sy >= 2) {
        return [
            ..._.times(12 - sm).map(m => YYYYMM(sy, m + sm + 1)),
            ..._.flatten(
                _.times(ey - sy - 1).map(y => _.times(12).map(m => YYYYMM(y + ey - 2, m + 1)))
            ),
            ..._.times(em + 1).map(m => YYYYMM(ey, m + 1)),
        ]
    }
    throw Error('Incorrect start or end')
}

let index = 0
const fullSeries = []
listMonth(start, end).forEach(mstr => {
    if (index < series.length && mstr === series[index].name) {
        fullSeries.push(series[index++])
    } else {
        fullSeries.push({ name: mstr, value: 0 })
    }
})

最后补全的fullSeries可用于绘制时间线完整的图表。

0

评论区