需求
如下图,保存的时候对表格中存在的断档日期进行提醒
预期效果
有准确的提醒
实现
数据瞎造的,跟贴图不一致,不用纠结哈
// 默认日期全部合法,即开始日期小于结束日期
let tableData = [
{
dateStart: "2015-02-01",
dateEnd: "2016-07-08",
},
{
dateStart: "2013-02-01",
dateEnd: "2014-07-08",
},
{
dateStart: "2016-07-09",
dateEnd: "2018-04-05",
},
{
dateStart: "2015-05-09",
dateEnd: "2019-01-02",
},
{
dateStart: "2019-05-09",
dateEnd: "2021-01-02",
},
]; // 初始数据
// 数组从小到大排序
// arr: 排序数组
// minToMax:是否从小到大排序,默认true
function getSort(arr, minToMax = true) {
for (var i = 0; i < arr.length; i++) {
for (var j = i + 1; j < arr.length; j++) {
let changeNum = () => {
let current = arr[i];
arr[i] = arr[j];
arr[j] = current;
};
if (minToMax) {
// 从小到大排序
if (arr[i] > arr[j]) {
changeNum();
}
} else {
// 从大到小排序
if (arr[i] < arr[j]) {
changeNum();
}
}
}
}
return arr;
}
let
minDate = getSort(tableData.map((it) => it.dateStart))[0], // 获取日期最小值
maxDate = getSort(
tableData.map((it) => it.dateEnd),
false
)[0], // 获取日期最大值
initDates = [[minDate, maxDate]]; // 初始化表格最大范围时间区间,即均未被使用的时间区间
// 使用排除法,只保留未使用的时间区间,区间如果被使用则移除该段区间;
// 如果是完整的不断档的日期,最后initDates应该为空数组;否则为断档日期数组;
tableData.forEach((it, index) => {
let newDates = [];
initDates.forEach((date) => {
// 如果判定日期时间段不在当前未使用日期内
if (it.dateEnd <= date[0] || it.dateStart >= date[1]) {
newDates.push(date);
}
// 如果判定日期时间段在当前未使用日期内
if (date[0] < it.dateStart && it.dateEnd < date[1]) {
newDates.push([date[0], it.dateStart]);
newDates.push([it.dateEnd, date[1]]);
}
// 如果判定只有日期时间段开始时间在当前未使用日期内
if (
date[0] < it.dateStart &&
it.dateStart < date[1] &&
it.dateEnd >= date[1]
) {
newDates.push([date[0], it.dateStart]);
}
// 如果判定只有日期时间段结束时间在当前未使用日期内
if (
date[0] < it.dateEnd &&
it.dateEnd < date[1] &&
it.dateStart <= date[0]
) {
newDates.push([it.dateEnd, date[1]]);
}
});
initDates = newDates;
});
console.log(initDates, 'initDates')
// [
// [
// "2014-07-08",
// "2015-02-01"
// ],
// [
// "2019-01-02",
// "2019-05-09"
// ]
// ]
// 即 2014-07-08 到 2015-02-01 有断档,2019-01-02 到 2019-05-09 有断档;
百度了下没找到现成的,自己动手,记录一下,留爪~
把一个大佬的方法也记录在这里,随意自取
function toDays(date) {
return Math.floor((new Date(date).getTime() - new Date(2000, 1, 1).getTime()) / (1000 * 3600 * 24))
}
function toDate(day) {
return new Date(day * 1000 * 3600 * 24 + new Date(2000, 1, 1).getTime())
}
function format(date) {
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`
}
function solution(ranges) {
// 格式化
ranges = ranges.map(item => [toDays(item.dateStart), toDays(item.dateEnd)])
// 获取初始区间
const [min, max] = ranges.reduce((prev, curr) => {
return [Math.min(prev[0], curr[0]), Math.max(prev[1], curr[1])]
}, [Infinity, -Infinity])
// 开始计算
return _solution([[min, max, 0]], ranges)
}
function _solution(arr, ranges) {
ranges.forEach(([min, max]) => {
// 获取 min 落在的区间 index
let minIndex = arr.findIndex(item => min >= item[0] && min <= item[1])
// 获取 max 落在的区间 index
let maxIndex = arr.findIndex(item => max >= item[0] && max <= item[1])
// min 落在的区间
let minRange = arr[minIndex]
// max 落在的区间
let maxRange = arr[maxIndex]
if (minIndex === maxIndex) { // 左右端点落到同一个未使用区间
if (minRange[2] === 0) { // 该区间未使用,则拆分成 3 个区间
arr.splice(minIndex, 1, [minRange[0], min - 1, 0], [min, max, 1], [max + 1, minRange[1], 0])
}
} else { // 左右端点落到两个区间
// 如果左侧区间已经使用,则 min 可认为是左侧区间的左端点
if (minRange[2] === 1) {
min = minRange[0]
}
// 如果右侧区间已经使用,则 max 可以认为是右侧区间的右端点
if (maxRange[2] === 1) {
max = maxRange[1]
}
// 左侧区间保留未使用的
arr.splice(minIndex, 1, [minRange[0], min - 1, 0])
// 右侧区间保留未使用的
arr.splice(maxIndex, 1, [max + 1, maxRange[1], 0])
// 中间的区间做合并处理
arr.splice(minIndex, maxIndex - minIndex - 1, [min, max, 1])
}
})
// 最终的结果需要过滤
// 过滤出未使用的区间 => item[2] === 0
// 过滤出合法区间 => item[0] <= item[1]
return arr.filter(item => item[2] === 0 && item[0] <= item[1])
}
const input = [
{
dateStart: "2015-02-01",
dateEnd: "2016-07-08",
},
{
dateStart: "2013-02-01",
dateEnd: "2014-07-08",
},
{
dateStart: "2016-07-09",
dateEnd: "2018-04-05",
},
{
dateStart: "2015-05-09",
dateEnd: "2019-01-02",
},
{
dateStart: "2019-05-09",
dateEnd: "2021-01-02",
},
]
const output = solution(input)
output.forEach(item => {
console.log(`${format(toDate(item[0]))} - ${format(toDate(item[1]))}`)
})
// 这个方法过滤了边界,所以输出不一样
// 2014-7-9 - 2015-1-31
// 2019-1-3 - 2019-5-8