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

如何更优雅地实现获取有序数组中目标值对应范围的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:05:40