How Lodash sortBy Handles Stable Sorting
Lodash’s _.sortBy function creates a new array of sorted
elements based on specified iteratees, guaranteeing a stable sort across
all JavaScript environments. A sorting algorithm is considered stable
when elements with identical sorting keys retain their original relative
order in the output. This article examines the internal mechanisms
Lodash uses to enforce stable sorting, how it compares equal values, and
why this behavior ensures consistency regardless of engine
implementation.
The Principle of Stable Sorting in Lodash
When sorting an array of objects, items often have duplicate sort criteria. In an unstable sort, the relative positioning of these equal items is not guaranteed and can change unpredictably.
Lodash ensures stability by tracking the original position of every
element before performing any comparisons. Even if multiple objects
produce identical values when evaluated against the sorting iteratees,
_.sortBy relies on the original insertion order as an
automatic tiebreaker.
The Underlying Mechanism: Index Tracking
To maintain stability across various runtimes, Lodash internally normalizes the collection into intermediate data structures before sorting. The process works as follows:
- Mapping Criteria and Indexes: Lodash iterates through the input collection and transforms each item into an internal object. This internal representation stores the computed sort criteria values alongside the item's original array index.
- Multi-Tier Comparison: During the sorting phase, Lodash performs an ascending comparison on the computed criteria. If the criteria for two items are identical, Lodash falls back to comparing their recorded original indexes.
- Restoring the Output: Because the original index is used as the final tiebreaker, an item that appeared earlier in the original array will always be placed before an item with the same key that appeared later. Finally, Lodash unwraps the sorted intermediate structures back into the final result array.
const users = [
{ id: 1, group: 'A' },
{ id: 2, group: 'B' },
{ id: 3, group: 'A' }
];
const sorted = _.sortBy(users, 'group');
// Result:
// [
// { id: 1, group: 'A' },
// { id: 3, group: 'A' },
// { id: 2, group: 'B' }
// ]
// Notice that id: 1 still precedes id: 3 because their original order is preserved.Cross-Browser Reliability
Historically, the ECMAScript specification did not require native
Array.prototype.sort to be stable. Browsers implemented
different algorithms; for example, older versions of Google's V8 engine
used an unstable Quicksort for arrays containing more than ten
elements.
Lodash implemented its own index-preservation mechanism to guarantee
identical, deterministic behavior across all browsers and Node.js
versions. While ECMAScript 2019 (ES10) eventually mandated that native
JavaScript sorting must be stable (often via TimSort), Lodash's
_.sortBy retains its explicit fallback logic, providing
complete predictability across legacy and modern execution environments
alike.