Lodash源码阅读-sortedLastIndexOf

Lodash 源码阅读-sortedLastIndexOf

概述

sortedLastIndexOf 是 Lodash 里的一个查找函数,它在已排序的数组中找出某个值最后一次出现的位置。与普通的 lastIndexOf 相比,它利用数组已排好序的特点,用二分查找的方式快速定位,大大提高了搜索效率(时间复杂度从 O(n) 降到 O(log n))。

这个函数特别适合处理有重复元素的已排序数组。比如在 [1, 2, 3, 3, 3, 4] 这样的数组中,查找 3 的最后一个位置,普通的 lastIndexOf 需要从后往前遍历,而 sortedLastIndexOf 能快速定位到索引 4。

前置学习

依赖函数

  • baseSortedIndex :内部的二分查找函数,通过设置 retHighest 参数为 true 可以找到值应该插入的最高位置
  • eq:判断两个值是否相等的函数,能正确处理各种特殊情况(比如 NaN)

技术知识

  • 二分查找:在有序数据中高效查找的算法,每次把范围缩小一半
  • 相等性比较:JavaScript 中判断值相等的方法和陷阱
  • 边界检查:处理空数组和特殊输入值的技巧

源码实现

javascript 复制代码
/**
 * This method is like `_.lastIndexOf` except that it performs a binary
 * search on a sorted `array`.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Array
 * @param {Array} array The array to inspect.
 * @param {*} value The value to search for.
 * @returns {number} Returns the index of the matched value, else `-1`.
 * @example
 *
 * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5);
 * // => 3
 */
function sortedLastIndexOf(array, value) {
  var length = array == null ? 0 : array.length;
  if (length) {
    var index = baseSortedIndex(array, value, true) - 1;
    if (eq(array[index], value)) {
      return index;
    }
  }
  return -1;
}

实现思路

sortedLastIndexOf 的思路很巧妙:

  1. 先检查数组是否为空,空的就直接返回 -1
  2. baseSortedIndex 函数找值应该插入的最高位置 (关键是传入第三个参数 true
  3. 把得到的索引减 1,就是最后一个可能等于目标值的位置
  4. 检查这个位置的值是否等于要找的值:
    • 是的话,返回这个位置
    • 不是的话,说明数组里没有这个值,返回 -1

这种方法比从后往前遍历要高效得多,特别是在大数组中查找时。

源码解析

边界检查

javascript 复制代码
var length = array == null ? 0 : array.length;
if (length) {
  // 查找逻辑
}
return -1;

先看数组是否为空:

  • 如果传入的 arraynullundefined,就把长度设为 0
  • 只有数组有内容时才进行查找
  • 如果数组是空的,直接返回 -1

查找和匹配

javascript 复制代码
var index = baseSortedIndex(array, value, true) - 1;
if (eq(array[index], value)) {
  return index;
}

这部分是核心逻辑:

  1. 调用 baseSortedIndex 并传入 true 作为第三个参数(这是重点!)
    • 这会找到值应该插入的最高位置,也就是所有等于该值的元素之后的那个位置
  2. 将索引减 1,得到最后一个可能匹配的元素位置
  3. eq 函数检查该位置的值是否等于要找的值
  4. 如果匹配,返回这个位置;否则返回 -1

注意这里与 sortedIndexOf 不同,没有 index < length 的判断,原因是:

  • sortedLastIndexOf 中,我们是从 baseSortedIndex 返回的索引减 1
  • 这个索引最大只可能是数组长度,减 1 后最大是 length - 1
  • 所以 index 一定小于 length,不需要额外判断
  • 而且如果值比数组中所有元素都小,baseSortedIndex 会返回 0,减 1 后变成 -1,此时访问 array[-1] 会返回 undefinedeq(undefined, value) 会返回 false,函数正确返回 -1

这种设计既简洁又安全,避免了不必要的条件检查。

与 sortedIndexOf 的区别

这个函数与 sortedIndexOf 非常相似,主要区别在于:

  • sortedIndexOf第一个匹配的位置
  • sortedLastIndexOf最后一个匹配的位置

实现上的区别就是在调用 baseSortedIndex 时,传入 true 作为第三个参数,以及后续处理索引的方式不同。

总结

sortedLastIndexOf 这个小函数再次展示了算法选择的重要性:

  1. 巧妙利用二分查找

    • 通过设置 retHighest 参数,可以找到值的最后位置而不是第一个位置
    • 将 O(n) 的线性查找优化为 O(log n) 的二分查找
  2. 代码复用的艺术

    • sortedIndexOf 共用核心的 baseSortedIndex 函数
    • 通过参数和简单的后处理,实现了完全不同的功能
  3. 设计上的一致性

    • 与 JavaScript 原生的 lastIndexOf 接口保持一致
    • 函数命名清晰地表明了它的用途和使用场景

sortedLastIndexOfsortedIndexOf 共同构成了处理已排序数组的高效工具,特别适合处理有重复元素的场景,是 Lodash 设计精巧的又一例证。

相关推荐
打小就很皮...20 分钟前
HBuilder 发行Android(apk包)全流程指南
前端·javascript·微信小程序
集成显卡1 小时前
PlayWright | 初识微软出品的 WEB 应用自动化测试框架
前端·chrome·测试工具·microsoft·自动化·edge浏览器
前端小趴菜052 小时前
React - 组件通信
前端·react.js·前端框架
Amy_cx2 小时前
在表单输入框按回车页面刷新的问题
前端·elementui
dancing9993 小时前
cocos3.X的oops框架oops-plugin-excel-to-json改进兼容多表单导出功能
前端·javascript·typescript·游戏程序
后海 0_o3 小时前
2025前端微服务 - 无界 的实战应用
前端·微服务·架构
Scabbards_3 小时前
CPT304-2425-S2-Software Engineering II
前端
小满zs3 小时前
Zustand 第二章(状态处理)
前端·react.js
程序猿小D3 小时前
第16节 Node.js 文件系统
linux·服务器·前端·node.js·编辑器·vim
萌萌哒草头将军3 小时前
🚀🚀🚀Prisma 发布无 Rust 引擎预览版,安装和使用更轻量;支持任何 ORM 连接引擎;支持自动备份...
前端·javascript·vue.js