How Lodash Values Sorts Unordered Numeric Keys
This article examines how the Lodash utility function
_.values structurally orders elements when extracting
values from an object containing unordered numeric keys. It details the
interaction between Lodash's internal property traversal methods and the
ECMAScript specifications that dictate key enumeration order in
JavaScript runtimes.
When you pass an object with out-of-order numeric keys to
_.values, the resulting array contains the values sorted in
ascending numerical order of those keys, regardless of their original
insertion order. For example, passing
{ '10': 'c', '2': 'b', '1': 'a' } into
_.values returns ['a', 'b', 'c'].
The ECMAScript OrdinaryOwnPropertyKeys Specification
Lodash does not implement a custom sorting algorithm inside
_.values. Instead, the observed sorting behavior is a
direct consequence of how modern JavaScript engines enumerate object
properties according to the ECMAScript 2015 (ES6) specification and its
subsequent revisions.
Under the ES specification (specifically the internal method
[[OwnPropertyKeys]] or
OrdinaryOwnPropertyKeys), property keys are ordered
deterministically across three distinct phases:
- Integer Indices: Keys that are non-negative integer
strings (indices from
0to2^32 - 2) are processed first, sorted in ascending numerical order. - String Keys: Standard string properties are processed next, strictly in chronological insertion order.
- Symbol Keys: Symbol properties are processed last, also in chronological insertion order.
Because property keys that can be parsed as non-negative 32-bit integers are always placed in the first phase and sorted numerically, the runtime normalizes their sequence before Lodash retrieves them.
Internal Lodash Implementation
Internally, _.values delegates to
baseValues, which extracts the values of an object
corresponding to the keys returned by _.keys. The
_.keys method, in turn, relies on native
Object.keys() when running in modern environments, or
internally traverses enumerable own properties using standard iteration
protocols.
Since native implementations of Object.keys(),
for...in loops, and Reflect.ownKeys() all
conform to the OrdinaryOwnPropertyKeys ordering rule,
Lodash retrieves the numeric keys in their canonical, ascending order.
Lodash then maps each key to its corresponding property value in the
exact order the keys were retrieved.
Practical Implications
When designing data structures intended for consumption via
_.values, you cannot rely on insertion order if the object
keys are integer-like numbers. If preserving the original insertion
sequence of numeric properties is required, an array of objects or an
ES6 Map should be used instead of an ordinary JavaScript
object, as Map guarantees iteration based solely on entry
insertion order.