资讯动态

es-toolkit compat 的 sortedLastIndexBy:基于变换函数的二分查找与最高插入位置详解

发布时间:2026/9/15 19:01:10 来源:尧图企业网站定制
es-toolkit compat 的 sortedLastIndexBy基于变换函数的二分查找与最高插入位置详解【免费下载链接】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-toolkitsortedLastIndexBy是 es-toolkit 的 compat 兼容模块中提供的 Lodash 兼容函数用于在已排序数组中先对每个元素与待插入值应用一个变换函数iteratee再通过二分查找定位最高的插入下标。当数组中存在重复值时它返回最后一个相同元素之后的位置是sortedIndexBy返回最低插入位置的镜像版本。读完本文你将掌握它的调用签名、四种 iteratee 写法、边界值语义以及它在 sortedIndexBy.ts 中完整的二分查找实现原理并能判断何时该用它、何时应改用原生二分查找。功能概述与函数签名sortedLastIndexBy的行为与sortedIndex一致但额外接受一个iteratee该函数会先被应用于value和数组中的每个元素以计算它们的“排序排名”。这一点在源码注释中有明确说明见 sortedLastIndexBy.ts。const index sortedLastIndexBy(array, value, iteratee);函数签名节选自 sortedLastIndexBy.tsexport function sortedLastIndexByT( array: ArrayLikeT | null | undefined, value: T, iteratee: ValueIterateeT ): number;array待查找的有序数组类型为ArrayLikeT | null | undefined这意味着它同样接受类数组对象如arguments、字符串等value要插入的目标值iteratee变换函数ValueIterateeT其完整类型定义位于 _internal/ValueIteratee.ts((value: T) unknown) | (PropertyKey | [PropertyKey, any] | PartialShallowT)即可以是函数、属性名、属性值对或部分对象返回值为number类型——该值应插入的最高下标。使用示例按对象属性查找最高插入位置当数组元素是对象且按某个属性排序时可以传入属性名字符串作为 iterateeimport { sortedLastIndexBy } from es-toolkit/compat; // 在按 x 属性排序的对象数组中查找最后一个插入位置 const objects [{ x: 4 }, { x: 5 }, { x: 5 }]; sortedLastIndexBy(objects, { x: 5 }, x); // 返回 3即最后一个 x: 5 之后的位置这里插入值{ x: 5 }会先被变换为5随后与各元素的x属性4、5、5比较。由于是“最高”位置即便已经有重复的5结果仍是最后一个5之后的下标 3。使用函数变换import { sortedLastIndexBy } from es-toolkit/compat; const numbers [10, 20, 20, 30]; sortedLastIndexBy(numbers, 20, n n); // 返回 3使用恒等函数n n时语义等同于sortedLastIndex在[10, 20, 20, 30]中插入20应放在下标 3两个 20 之后、30 之前。空数组与 null / undefined 的处理对于null、undefined或长度为 0 的数组函数直接返回 0import { sortedLastIndexBy } from es-toolkit/compat; sortedLastIndexBy(null, { x: 1 }, x); // 0 sortedLastIndexBy(undefined, { x: 1 }, x); // 0这一行为在 sortedIndexBy.ts 中有明确实现isNil(array) || array.length 0时提前返回 0。相应地sortedLastIndexBy.spec.ts 中的测试也验证了“长度为 0 时不应调用 iteratee”这一约定——即便传入一个会抛异常的 iteratee函数也只返回 0 而不会触发调用。参数详解array有序数组类型ArrayLikeT | null | undefined前提条件数组必须是已排序的。该函数基于二分查找对无序数组不会先排序结果不可预期这一点在官方文档中已被明确标注。value待插入值类型T与数组元素同类型在比较前会先经过iteratee变换因此比较的实际上是iteratee(value)与iteratee(array[mid])。iteratee变换函数可选iteratee支持四种形式最终都会经由 util/iteratee.ts 归一化为一个标准函数形式说明归一化结果函数直接作为变换函数使用如n n、obj obj.x原样返回属性名string / number / symbol提取元素的该属性值如xproperty(value)属性值对[PropertyKey, any]判断元素属性是否等于给定值返回布尔值matchesProperty(key, val)部分对象PartialT判断元素是否匹配部分对象返回布尔值matches(obj)若 iteratee 缺省或传入null/undefined则回退为恒等函数identity见 iteratee.ts 与 sortedIndexBy.ts 的默认参数iteratee identity。这也解释了文档中“iteratee 为可选参数”的约定。返回值语义返回值为number即value应插入的最高下标数组有序且存在重复值时返回最后一个相等元素之后的位置数组中不存在相等值时返回首个大于value的元素下标即应插入点数组为null/undefined/空时返回 0。作为对照sortedIndexBy返回最低插入下标二者的行为差异来自二分查找比较条件中与的区别详见下文实现解析。两者的完整对比例子可参考 sortedIndexBy 文档。源码实现深度解析入口委托给 sortedIndexBy 并开启 retHighestsortedLastIndexBy本身并不重复实现二分查找而是把工作委托给sortedIndexBy并传入第四个参数true作为“返回最高位置”标志见 sortedLastIndexBy.tsexport function sortedLastIndexByT, R( array: ArrayLikeT | null | undefined, value: T, iteratee?: IterateeT, R ): number { return sortedIndexBy(array, value, iteratee, true); }从源码结构看retHighest这个布尔标志是二者的唯一分叉点。核心sortedIndexBy 中的二分查找循环真正的算法位于 sortedIndexBy.ts整体流程如下空值短路isNil(array) || array.length 0时返回 0初始化区间low 0high array.length归一化 iteratee调用iterateeToolkit(iteratee)得到统一函数预计算变换值transformedValue iterateeFunction(value)只计算一次预判特殊值对变换后的值依次检查isNaN、isNull、isSymbol、isUndefined得到四个布尔标志循环二分mid Math.floor((low high) / 2)对array[mid]再次应用 iteratee 得到computed同样检查其是否为 undefined/null/NaN/symbol决定收缩方向setLow为真则low mid 1否则high mid收尾返回Math.min(high, MAX_ARRAY_INDEX)其中MAX_ARRAY_LENGTH 42949672952³² − 1。对于普通数值比较关键的比较条件是sortedIndexBy.tssetLow retHighest ? computed! transformedValue : computed! transformedValue;retHighest falsesortedIndexBy时用低指针会停在第一个相等元素处得到最低插入位置retHighest truesortedLastIndexBy时用遇到相等元素也继续右移最终收敛到最后一个相等元素之后得到最高插入位置。特殊值NaN / null / symbol / undefined的比较语义Lodash 对特殊值的处理相当细致源码用多级条件分支复刻了这一语义sortedIndexBy.ts变换后的值为NaN时setLow retHighest || othIsReflexive即查找最高位置时总是右移变换后的值为undefined时要求对端元素“自反”非 NaN且最高模式或对端非 undefined变换后的值为null时要求对端非 NaN、非 undefined且最高模式或对端非 null变换后的值为symbol时要求对端非 NaN、非 undefined、非 null且最高模式或对端非 symbol对端为null或symbol时setLow false一律左移。这些分支保证了与 Lodash 完全一致的边界行为——NaN被视为最小、undefined次之、null与symbol各有其排序位置——这是sortedLastIndexBy作为 compat 函数“兼容优先”的核心价值所在。对超大数组的支持实现中还预留了对超出MAX_ARRAY_LENGTH / 2的超大稀疏数组的支持返回时以Math.min(high, MAX_ARRAY_INDEX)兜底防止下标越界。这一点在测试 sortedLastIndexBy.spec.ts 中得到验证测试分别构造了长度为Math.ceil(MAX_ARRAY_LENGTH / 2)和MAX_ARRAY_LENGTH的稀疏数组并断言二分步数稳定在 3233 次之间——这正是对长度 2³² 级别数组做二分查找应有的对数复杂度特征约 log₂(2³²) ≈ 32。性能警告与替代方案官方文档在函数说明前给出了明确的性能警告由于复杂的 iteratee 处理与类型转换sortedLastIndexBy运行较慢建议直接实现更快、更现代的二分查找与变换函数。从源码可以印证这一判断每轮循环都要调用iterateeFunction(array[mid])而 iteratee 归一化、isNaN/isNull/isSymbol/isUndefined等多次类型检查都会带来额外开销。若你的场景仅需对原始值而非变换值做插入定位优先使用更轻量的sortedLastIndexsortedLastIndex.ts它针对普通数字走快速路径仅在值非数字、为 NaN 或数组超大时回退到sortedLastIndexBy。若连 Lodash 兼容语义都不需要直接手写一个比较的二分查找是性能最优的选择。测试用例验证sortedLastIndexBy.spec.ts 覆盖了四个关键行为iteratee 参数正确性sortedLastIndexBy([30, 50], 40, fn)中iteratee 收到的第一个参数就是待插入值40而非数组元素属性名简写sortedLastIndexBy(objects, { x: 40 }, x)对[{ x: 30 }, { x: 50 }]返回 1空数组不调用 iteratee长度为 0 时直接返回 0超大数组支持二分步数符合对数复杂度预期且非有限数值NaN/undefined在超大稀疏数组中也有明确语义。这些测试从侧面印证了本文前述的签名约定、空值短路与特殊值处理逻辑。小结sortedLastIndexBy是 es-toolkit compat 模块中面向“带变换的二分查找”场景的 Lodash 兼容实现它以sortedIndexBy(..., retHighest true)委托方式复用核心算法通过比较语义返回最高插入位置并完整复刻了 NaN/null/symbol/undefined 的特殊排序规则。在实际项目中请先确认数组已排序并优先考虑是否真的需要 iteratee 变换——如果不需要sortedLastIndex或手写二分查找会是更高效的选择。相关实现与测试可继续查阅 sortedIndexBy.ts、sortedLastIndex.ts、iteratee.ts 以及 sortedLastIndexBy.spec.ts。【免费下载链接】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),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价