如何以声明式方式识别数组中超出范围值的起止索引区间?
问题描述
我有三个长度为n的数组:
let values = [5, 5, 5, 6, 7, 6, 5, 4, 3, 3, 4, 5, 5]; let min_arr = [3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3]; let max_arr = [7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7]; let n = values.length;
需要找出所有区间的起止索引,这些区间内存在min_arr[i] > values[i]或max_arr[i] < values[i]的情况。
示例中的区间说明:
- 第一个区间:
values[4] = 7 > 6 = max_arr[4],但values[5] = 6未超出范围,因此起止索引为start = 4,end = 5。 - 第二个区间:
values[8]和values[9]都小于对应min_arr的值,但values[10]未超出范围,因此起止索引为start = 8,end = 10。
预期输出:[[4, 5], [8, 10]](注:最后一个结束索引大于n也无妨)
我目前的实现思路是先提取所有超出范围的索引,再合并连续索引生成区间:
// 第一步:获取所有超出范围的索引 let temp = values .map((e, i) => min_arr[i] > e || max_arr[i] < e ? i : undefined) .filter((e) => e); console.log(temp);
输出:
[4, 8, 9]
// 第二步:将连续索引转换为区间 let res = []; let start = temp[0]; for (let i = 0; i < temp.length; i++) { if (i + 1 == temp.length) { res.push([start, temp[i] + 1]); break; } if (temp[i] + 1 != temp[i + 1]) { res.push([start, temp[i] + 1]); start = temp[i + 1]; } } console.log(res);
输出:
[[4, 5], [8, 10]]
作为JavaScript新手,我觉得这种方法比较繁琐,想知道如何用声明式方式实现整个过程?
声明式实现方案
可以利用数组的reduce方法完成从判断范围到生成区间的全流程,全程采用声明式风格,逻辑更连贯:
方案一:直接遍历values生成区间
const result = values.reduce((acc, val, idx) => { const isOutOfRange = min_arr[idx] > val || max_arr[idx] < val; const lastInterval = acc[acc.length - 1]; if (isOutOfRange) { // 当前索引超出范围,且无正在处理的区间时,新建区间 if (!lastInterval) { acc.push([idx, null]); } } else if (lastInterval && lastInterval[1] === null) { // 当前索引未超出范围,但存在未闭合的区间,用当前索引闭合它 lastInterval[1] = idx; } return acc; }, []); // 处理最后一个未闭合的区间(比如数组末尾连续超出范围的情况) if (result.length > 0 && result[result.length - 1][1] === null) { result[result.length - 1][1] = values.length; } console.log(result);
输出:
[[4, 5], [8, 10]]
代码说明
- 遍历过程中实时判断当前索引是否超出范围,动态维护区间列表;
- 用
null标记未闭合的区间,遇到第一个未超出范围的索引时完成闭合; - 遍历结束后处理可能遗留的未闭合区间,避免遗漏。
方案二:先生成标记数组再处理区间
如果需要更清晰的步骤拆分,可以先生成"是否超出范围"的标记数组,再用reduce处理标记生成区间:
// 第一步:生成标记数组,true表示当前索引超出范围 const outOfRangeFlags = values.map((val, idx) => min_arr[idx] > val || max_arr[idx] < val); // 第二步:基于标记数组生成区间 const result = outOfRangeFlags.reduce((acc, flag, idx) => { const lastInterval = acc[acc.length - 1]; if (flag) { // 当前索引超出范围,且无正在处理的区间时,新建区间起始点 if (!lastInterval) { acc.push([idx]); } } else if (lastInterval && lastInterval.length === 1) { // 当前索引未超出范围,闭合之前的区间 lastInterval.push(idx); } return acc; }, []); // 处理最后一个未闭合的区间 if (result.length > 0 && result[result.length - 1].length === 1) { result[result.length - 1].push(values.length); } console.log(result);
输出同样符合预期。
内容的提问来源于stack exchange,提问作者rochard4u
相关产品推荐
相关产品推荐

