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 设计精巧的又一例证。

相关推荐
JiangJiang5 分钟前
🚀 Vue人看React useRef:它不只是替代 ref
javascript·react.js·面试
1024小神10 分钟前
在GitHub action中使用添加项目中配置文件的值为环境变量
前端·javascript
龙骑utr14 分钟前
qiankun微应用动态设置静态资源访问路径
javascript
Jasmin Tin Wei15 分钟前
css易混淆的知识点
开发语言·javascript·ecmascript
齐尹秦18 分钟前
CSS 列表样式学习笔记
前端
wsz777722 分钟前
js封装系列(一)
javascript
Mnxj22 分钟前
渐变边框设计
前端
用户76787977373225 分钟前
由Umi升级到Next方案
前端·next.js
快乐的小前端26 分钟前
TypeScript基础一
前端
北凉温华27 分钟前
UniApp项目中的多服务环境配置与跨域代理实现
前端