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

相关推荐
apcipot_rain2 小时前
【应用密码学】实验五 公钥密码2——ECC
前端·数据库·python
油丶酸萝卜别吃2 小时前
OpenLayers 精确经过三个点的曲线绘制
javascript
ShallowLin2 小时前
vue3学习——组合式 API:生命周期钩子
前端·javascript·vue.js
Nejosi_念旧2 小时前
Vue API 、element-plus自动导入插件
前端·javascript·vue.js
互联网搬砖老肖3 小时前
Web 架构之攻击应急方案
前端·架构
pixle03 小时前
Vue3 Echarts 3D饼图(3D环形图)实现讲解附带源码
前端·3d·echarts
麻芝汤圆4 小时前
MapReduce 入门实战:WordCount 程序
大数据·前端·javascript·ajax·spark·mapreduce
juruiyuan1115 小时前
FFmpeg3.4 libavcodec协议框架增加新的decode协议
前端
Peter 谭6 小时前
React Hooks 实现原理深度解析:从基础到源码级理解
前端·javascript·react.js·前端框架·ecmascript
周胡杰6 小时前
鸿蒙接入flutter环境变量配置windows-命令行或者手动配置-到项目的创建-运行demo项目
javascript·windows·flutter·华为·harmonyos·鸿蒙·鸿蒙系统