Lodash sortBy Order for Identical Values

When sorting collections using Lodash's _.sortBy, encountering identical values or contradictory object property evaluations can raise questions about how elements are positioned in the final output. In these scenarios, the final index placement is strictly governed by the algorithmic stability of Lodash's sorting implementation, which relies on the original index order of the items in the source array as the ultimate tie-breaker.

Algorithmic Stability in Lodash

Lodash implements a stable sorting algorithm. In computer science, a sorting algorithm is considered stable if two elements with equal keys appear in the sorted output in the same relative order as they appeared in the input dataset.

When you pass an array to _.sortBy, Lodash wraps each element in an internal object that captures two critical pieces of metadata:

  1. The computed criteria derived from the iteratee functions or property shorthands.
  2. The original index of the item within the source array.

Resolving Contradictory Object Properties

When an object contains conflicting properties—such as opposing flags or mixed values that resolve to the same derived sort weight—Lodash evaluates them based on the exact iteratees supplied:

  1. Iteratee Precedence: If multiple iteratees are provided (e.g., _.sortBy(users, ['propA', 'propB'])), Lodash checks criteria in sequential order. A contradiction only matters if an earlier iteratee fails to break the tie.
  2. Identical Evaluated Value: If custom iteratee logic or inherent property values resolve to identical primitive comparison values (e.g., both yield 0, true, or identical strings), Lodash considers the items indistinguishable by value.

The Final Determinant: Source Index Order

If two or more objects evaluate to identical values across all provided criteria, Lodash's internal comparator resolves the tie by comparing their original source indices.

Consider this example:

const items = [
  { id: 1, rank: 5, conflictFlag: true },
  { id: 2, rank: 5, conflictFlag: false }
];

// Sorting strictly by 'rank'
const result = _.sortBy(items, ['rank']);

Even though { id: 1 } and { id: 2 } hold contradictory values for conflictFlag, the sort iteratee only evaluates rank. Because both have a rank of 5, their computed keys are identical. Lodash bypasses the unreferenced contradictory property and relies on the original array sequence. Consequently, { id: 1 } retains a lower index than { id: 2 } in the output.

If contradictory properties are evaluated within a custom iteratee and yield the exact same return value, the exact same rule applies. The element encountered first during the initial traversal of the input array will always be placed ahead of subsequent identical matches in the final sorted array.