如何更优雅地实现获取有序数组中目标值对应范围的JS函数?
想优化一个有序列表区间查找函数,求更优雅的实现!
我最近写了个函数,功能是接收一个有序数字列表和数值X,返回数组[Y, Z]满足Y <= X < Z,还要处理输入值超出数组范围的边界情况。目前已经有了实现,但总觉得可以更精简、更有函数式风格,甚至写成一行?而且希望能支持一个额外的测试场景(不是必须,但有更好)。
先给大家看看我现在的实现:
现有实现代码
const getRange = (ranges, value) => { let rangeTopIndex = ranges.findIndex(range => value < range); // 数值大于数组最大值,取最后一个区间 if (rangeTopIndex == -1) { rangeTopIndex = ranges.length - 1 } // 数值小于数组最小值,取第一个区间 if (rangeTopIndex == 0) { rangeTopIndex = 1; } let rangeBottomIndex = rangeTopIndex - 1; return [ranges[rangeBottomIndex], ranges[rangeTopIndex]] };
对应的测试用例
describe('getRange', function() { it('should get the correct range', () => { // 中间值情况 expect(getRange([0, 0.2, 0.7, 1] , 0.5)).toEqual([0.2, 0.7]); expect(getRange([0, 0.2, 0.7, 1] , 0.1)).toEqual([0, 0.2]); expect(getRange([0, 0.2, 0.7, 1] , 0.2)).toEqual([0.2, 0.7]); // 边界值情况 expect(getRange([0, 0.2, 0.7, 1] , 0)).toEqual([0, 0.2]); expect(getRange([0, 0.2, 0.7, 1] , 1)).toEqual([0.7, 1]); // 超出数组范围的情况 expect(getRange([0, 0.2, 0.7, 0.8] , 0.9)).toEqual([0.7, 0.8]); expect(getRange([0.2, 0.3, 0.7, 0.8] , 0.1)).toEqual([0.2, 0.3]); // 额外测试场景(非必须,但希望支持) expect(getRange([0.2, 0.3, 0.3, 0.8] , 0.3)).toEqual([0.3, 0.3]); }); });
因为数据量很小,完全不用考虑性能问题,所以想问问有没有更优雅的写法?比如更精简、函数式一点,最好能支持上面那个额外的测试场景。
优化后的实现方案
方案一:可读性优先的函数式写法
这个版本逻辑清晰,同时支持了额外的重复值场景:
const getRange = (ranges, value) => { // 找到第一个大于value的元素索引 const firstGreaterIdx = ranges.findIndex(r => r > value); // 所有元素都<=value,返回最后两个元素 if (firstGreaterIdx === -1) { return [ranges.at(-2), ranges.at(-1)]; } // 所有元素都>value,返回前两个元素 if (firstGreaterIdx === 0) { return [ranges[0], ranges[1]]; } // 处理重复值的额外场景:value等于当前区间下限,且下限和上限相等 const lower = ranges[firstGreaterIdx - 1]; const upper = ranges[firstGreaterIdx]; if (lower === value && upper === value) { // 找到最后一个等于value的位置,取后续第一个不同的元素(或value本身) const lastEqualIdx = ranges.lastIndexOf(value); return [value, ranges[lastEqualIdx + 1] || value]; } // 普通场景返回对应区间 return [lower, upper]; };
方案二:精简到一行的写法(可读性稍弱)
如果追求极致精简,也可以压缩成一行,核心逻辑和上面一致:
const getRange = (r, v) => { const i = r.findIndex(n => n > v); return i === -1 ? [r.at(-2), r.at(-1)] : i === 0 ? [r[0], r[1]] : r[i-1] === v && r[i] === v ? [v, r[r.lastIndexOf(v)+1] || v] : [r[i-1], r[i]]; };
逻辑说明
- 核心还是用
findIndex快速定位第一个大于目标值的元素,和原实现思路一致,但用更简洁的条件分支处理边界 - 针对重复值的额外场景,通过
lastIndexOf找到最后一个相同值的位置,确保返回[value, value]的结果 - 用
at(-2)和at(-1)替代ranges[ranges.length-2],写法更简洁现代
内容的提问来源于stack exchange,提问作者micnil
相关产品推荐
相关产品推荐

