How Lodash sortedLastIndex Uses Binary Search

Lodash’s _.sortedLastIndex function determines the highest index at which a value should be inserted into a sorted array to preserve its sorted order. By leveraging an optimized variant of the binary search algorithm, the function reduces search time from linear \(O(n)\) to logarithmic \(O(\log n)\). This article breaks down the internal mechanics of _.sortedLastIndex, detailing how its comparison logic handles duplicates and the specific low-level JavaScript optimizations Lodash uses to maximize execution speed.

The Core Binary Search Mechanism

Standard binary search algorithms typically terminate as soon as an exact match is identified. In contrast, _.sortedLastIndex must find the last valid insertion point. This means that when it encounters elements identical to the search target, it must continue searching toward the right boundary of the array.

Internally, _.sortedLastIndex relies on a base binary search function (baseSortedIndex). The algorithm maintains two pointers: low, initialized to 0, and high, initialized to the array's length.

let low = 0;
let high = array.length;

while (low < high) {
  const mid = (low + high) >>> 1;
  if (array[mid] <= value) {
    low = mid + 1;
  } else {
    high = mid;
  }
}
return high;

The critical divergence from a basic binary search is the comparison condition array[mid] <= value:

When low and high converge, the resulting index represents the position immediately following any identical values already present in the collection.

Low-Level Optimizations in Lodash

Lodash implements several computational optimizations to make this search as fast as possible in modern JavaScript engines:

1. Bitwise Zero-Fill Right Shift (>>>)

Instead of computing the midpoint using standard arithmetic and rounding—such as Math.floor((low + high) / 2)—Lodash employs the unsigned right shift operator: (low + high) >>> 1.

This provides two direct benefits:

2. Specialized Base Methods

Lodash separates simple index lookups from complex iterations. While methods like _.sortedLastIndexBy accept an iteratee callback to resolve properties, _.sortedLastIndex delegates directly to a streamlined path that avoids callback invocation overhead. By eliminating dynamic function calls inside the while loop, the JavaScript V8 engine can inline the comparisons and optimize execution via JIT (Just-In-Time) compilation.

3. Graceful Handling of NaN

Standard JavaScript comparison operators fail when comparing values against NaN (since NaN <= value and NaN > value both evaluate to false). Lodash's internal implementation explicitly accounts for NaN and undefined values, ensuring they are treated as greater than any valid numeric or string value, thus preventing infinite loops and maintaining deterministic insertion points.

Algorithmic Complexity

By halving the search space on each iteration, _.sortedLastIndex performs at \(O(\log n)\) time complexity and \(O(1)\) auxiliary space complexity. Even on arrays containing millions of sorted items, the function resolves the correct insertion index in roughly 20 to 30 iterations, avoiding unnecessary memory allocations and minimizing cache misses.