Lodash sortedLastIndex: Purpose and Key Use Cases
Lodash's _.sortedLastIndex method is a binary search
utility designed to find the highest index at which a value can be
inserted into an already sorted array while maintaining its sort order.
This article covers how the method operates, how it differs from
_.sortedIndex, and the specific production use cases where
choosing the highest insertion index is critical, such as maintaining
First-In-First-Out (FIFO) ordering among duplicate keys and performing
binary-search-based range queries.
What is
_.sortedLastIndex?
The _.sortedLastIndex function uses a binary search
algorithm to evaluate a sorted array and determine where a given value
should be inserted.
_.sortedLastIndex(array, value)The key behavior is how it handles duplicate values:
_.sortedIndexreturns the first (lowest) index where the value can be inserted._.sortedLastIndexreturns the last (highest) index where the value can be inserted—meaning right after any existing elements with the same value.
const numbers = [10, 20, 20, 20, 30];
_.sortedIndex(numbers, 20); // Output: 1 (before existing 20s)
_.sortedLastIndex(numbers, 20); // Output: 4 (after existing 20s)Specific Use Cases
1. Maintaining FIFO Order for Equivalent Keys
The most common use case for _.sortedLastIndex is
maintaining a stable, chronologically ordered list of items that share
identical primary sort values.
If you maintain an array of tasks sorted by priority (e.g., Priority
1 to 5), new tasks with the same priority should logically be queued
behind previously added tasks of that same priority. Using
_.sortedIndex would place the new task before existing
identical priorities (LIFO). _.sortedLastIndex places the
new entry directly after existing duplicates (FIFO), ensuring fair,
stable queue management:
const priorityQueue = [1, 2, 2, 2, 3];
const newPriority = 2;
const insertAt = _.sortedLastIndex(priorityQueue, newPriority);
priorityQueue.splice(insertAt, 0, newPriority);
// Result: [1, 2, 2, 2, 2, 3] (appended to the end of the 2s)2. Determining Value Multiplicity and Ranges in \(O(\log n)\) Time
By pairing _.sortedIndex and
_.sortedLastIndex, you can determine the exact count and
index span of duplicate items in a massive sorted array without
iterating linearly.
- The start of the range is
_.sortedIndex(array, target). - The end of the range is
_.sortedLastIndex(array, target).
const data = [5, 10, 15, 15, 15, 15, 20, 25];
const target = 15;
const startIndex = _.sortedIndex(data, target); // 2
const endIndex = _.sortedLastIndex(data, target); // 6
const count = endIndex - startIndex; // 4
const occurrences = data.slice(startIndex, endIndex); // [15, 15, 15, 15]This pattern provides a pure \(O(\log
n)\) search time, which is significantly faster than using
filter or indexOf/lastIndexOf
combinations on large datasets.
3. Binning and Range Boundary Routing
When mapping continuous data into discrete bins (such as age brackets, tax brackets, or latency percentiles), the boundary conditions dictate whether edge values are included in the lower or upper bucket:
- Use
_.sortedIndexfor half-open intervals formatted as[lower, upper). - Use
_.sortedLastIndexfor half-open intervals formatted as(lower, upper].
This ensures that values equal to the bin boundary fall into the intended side of the interval without requiring manual comparison operators.