ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

es-toolkit 的 sortedLastIndex 详解:在排序数组中定位最靠后的插入位置(Lodash 兼容实现)

es-toolkit 的 sortedLastIndex 详解:在排序数组中定位最靠后的插入位置(Lodash 兼容实现) es-toolkit 的 sortedLastIndex 详解在排序数组中定位最靠后的插入位置Lodash 兼容实现【免费下载链接】es-toolkitA modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkitsortedLastIndex是 es-toolkit 的 Lodash 兼容层es-toolkit/compat中提供的一个数组工具函数给定一个已排序的数组和一个值它通过二分查找返回为了维持排序顺序该值应当插入的最靠后的索引。当数组中存在重复值时它返回最后一个重复值之后的位置这与返回最靠前插入位置的sortedIndex形成互补。本文以 sortedLastIndex 官方文档 为骨架结合 源码实现 与 测试用例完整讲解其用法、参数、返回值、边界行为与底层二分查找原理并延伸介绍其变体sortedLastIndexBy与关联函数sortedLastIndexOf。函数签名与核心语义const index sortedLastIndex(array, value);sortedLastIndex接受一个已排序数组和一个待插入的值返回该值应当插入的最高索引。其核心语义可以概括为三条规则存在重复值返回最后一个重复值之后的索引值不存在于数组中返回该值按排序顺序应当落入的位置值小于所有元素返回0插入到最前面。以下示例直观展示了这三种情况来自 官方文档import { sortedLastIndex } from es-toolkit/compat; // 存在重复值时返回最后一个 5 之后的位置 sortedLastIndex([4, 5, 5, 5, 6], 5); // 4 // 新值不存在时返回它应当插入的位置25 位于 30 之前 sortedLastIndex([10, 20, 30], 25); // 2 // 值比所有元素都小时插入到最前面 sortedLastIndex([1, 2, 3], 0); // 0当传入的array为null或undefined时函数直接返回0import { sortedLastIndex } from es-toolkit/compat; sortedLastIndex(null, 1); // 0 sortedLastIndex(undefined, 1); // 0这一行为与 sortedLastIndex.ts 源码中的第一行判断完全一致if (isNil(array)) return 0;其中isNil同时覆盖null与undefined。参数与返回值说明参数参数类型说明arrayArrayLikeT \| null \| undefined已排序的数组。注意传入未排序数组会产生错误结果因为函数基于二分查找的前提是数组已排序valueT要插入的值类型签名中的ArrayLikeT值得注意函数不仅支持原生数组也支持类数组对象如{ length: 3, 0: 10, 1: 20, 2: 20 }。这与 Lodash 的行为保持一致。返回值number值应当插入的最高索引当数组为null或undefined时返回0。使用场景何时应该用 sortedLastIndexsortedLastIndex适用于以下典型场景在有序数据集中批量插入元素当你维护一个始终有序的数组需要在插入新元素后仍保持有序时可用它定位插入点再配合splice完成插入统计某个值在排序数组中的出现范围结合返回最靠前插入位置的sortedIndex可以用[sortedIndex(array, v), sortedLastIndex(array, v))精确刻画值v的完整区间这是实现排序数组中查询元素个数类似lowerBound/upperBound的标准手法实现按数值排名的边界查询例如在游戏分数榜、价格区间筛选等需要最后一个不越过阈值的位置的业务逻辑中直接使用。底层原理二分查找与快速路径源码级解析sortedLastIndex的实现位于 src/compat/array/sortedLastIndex.ts其核心是经典的寻找最右插入位置二分查找let low 0; let high array.length; while (low high) { const mid (low high) 1; const compute array[mid]; if (!isNull(compute) !isSymbol(compute) (compute as any) value) { low mid 1; // 当前元素 value说明插入点至少在 mid 之后 } else { high mid; // 否则收缩上界 } } return high;理解这段代码的关键在于比较条件只要array[mid] value成立就将下界推进到mid 1因此当存在多个与value相等的元素时查找不会停在第一个相等元素处而是持续向右推进最终high落在最后一个相等元素之后的位置——这正是最靠后插入位置的由来。(low high) 1使用无符号右移计算中点可以避免(low high)在极端长度下溢出的问题。两条优化/降级路径源码在进入二分查找前做了两处关键判断sortedLastIndex.ts快速路径当value是普通数字非NaN且数组长度不超过HALF_MAX_ARRAY_LENGTH即4294967295 1时走上面的轻量二分循环比较仅用一次运算降级路径当value不是数字、是NaN或数组长度超过HALF_MAX_ARRAY_LENGTH时函数委托给sortedLastIndexBy(array, value, value value)sortedLastIndexBy.ts由sortedLastIndexBy内部更复杂的比较逻辑处理字符串、对象、Symbol、null、undefined、NaN以及超大数组的排序语义。这种数字走快速路径、其他类型走通用路径的设计是为了在绝大多数常规用法数字数组下保持高性能同时严格对齐 Lodash 对各种混合类型的排序规则。对齐 Lodash 的排序规则从测试用例看边界行为测试文件 验证了大量边界行为其中最值得注意的是与sortBy保持一致的用例sortedLastIndex.spec.tsconst expected [1, 2, {}, symbol1, symbol2, null, undefined, NaN, NaN]; expect(sortedLastIndex(expected, 3)).toBe(2); // 数字 3 应插在 2 之后 expect(sortedLastIndex(expected, symbol3)).toBe(5); // Symbol 插在 symbol2 之后 expect(sortedLastIndex(expected, null)).toBe(6); // null 插在 symbol2 之后 expect(sortedLastIndex(expected, undefined)).toBe(7);// undefined 插在 null 之后 expect(sortedLastIndex(expected, NaN)).toBe(9); // NaN 插在最后这组断言揭示了一个重要事实es-toolkit 的sortedLastIndex复刻了 Lodash 的混合类型排序规则——数字排在字符串和对象之前Symbol 在null之前null在undefined之前NaN永远排在最后。如果你的业务代码依赖这种顺序例如由sortBy排序后的数据再做范围查询sortedLastIndex能保证结果与 Lodash 完全一致。此外测试还覆盖了[null, null]、[symbol1, symbol2]等全空值/全 Symbol 数组sortedLastIndex.spec.ts以及null/undefined数组统一返回[0, 0, 0]的边界情况sortedLastIndex.spec.ts。变体与关联函数sortedLastIndexBy带转换函数的最右插入位置当数组元素是对象需要按某个属性或经过函数转换后的值来决定排序位置时应使用sortedLastIndexBy其用法见 sortedLastIndexBy 文档import { sortedLastIndexBy } from es-toolkit/compat; // 按属性名 x 排序的对象数组找最后插入位置 const objects [{ x: 4 }, { x: 5 }, { x: 5 }]; sortedLastIndexBy(objects, { x: 5 }, x); // 3最后一个 x: 5 之后的位置 // 使用转换函数 const numbers [10, 20, 20, 30]; sortedLastIndexBy(numbers, 20, n n); // 3iteratee参数支持四种形态sortedLastIndexBy.ts转换函数(value) R、属性名字符串/数字/Symbol、[属性名, 值]形式的属性-值数组、以及部分匹配对象PartialT这与 Lodash 的 iteratee 简写约定一致。其底层实际委托给sortedIndexBy并传入retHighest truesortedLastIndexBy.tssortedIndexBy内部通过iterateeToolkit(iteratee)统一解析 iteratee并针对NaN、null、undefined、Symbol组合出完整的比较矩阵sortedIndexBy.ts。测试用例 还验证了 iteratee 只接收value一个参数、空数组时 iteratee 不会被调用以及超过MAX_ARRAY_LENGTH / 2的稀疏大数组4294967295长度下二分步数稳定在 32~33 次等行为。sortedLastIndexOf查找最后一个匹配值的索引sortedLastIndex还被 sortedLastIndexOf 复用后者先调用sortedLastIndex(array, value) - 1得到候选位置再用eq校验该位置元素是否与目标值相等相等则返回索引否则返回-1sortedLastIndexOf.ts。因此sortedLastIndex也是排序数组中查找最后一个重复值位置这一能力的基石。三个函数的导出sortedLastIndex、sortedLastIndexBy、sortedLastIndexOf均从 src/compat/compat.ts 统一导出可通过es-toolkit/compat入口直接引入。官方性能提示与使用建议官方文档在 sortedLastIndex 页面 顶部给出了明确警告建议直接实现二分查找。理由是sortedLastIndex为了兼容 Lodash 的复杂类型语义混合类型排序规则、Symbol/NaN/null 处理、超大数组保护、iteratee 解析等付出了额外的类型校验开销对于纯数字数组 明确排序规则的简单场景手写二分查找会更快。因此给出如下选型建议如果你的数据是同质数字数组且无需兼容 Lodash 语义直接用标准库lowerBound/upperBound或手写二分性能更优如果你的代码需要从 Lodash 迁移、需要处理混合类型排序语义或希望利用 iteratee 简写与sortedLastIndexOf等衍生能力则直接使用es-toolkit/compat的现成实现行为与 Lodash 完全对齐无论哪种方式都请务必保证传入的数组已按目标排序规则排好序否则二分查找会返回错误结果这是文档与源码共同强调的前提条件。【免费下载链接】es-toolkitA modern JavaScript utility library thats 2-3 times faster and up to 97% smaller, a major upgrade to lodash.项目地址: https://gitcode.com/GitHub_Trending/es/es-toolkit创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表