Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 854c03f

Browse files
committed
Update(orderBy): Guaranteed stable sort
Falls back on built-in comparison function if user-provided comparison function fails to break a tie between elements being ordered. Closes #14881 BREAKING CHANGE: Using `orderBy` with a custom `comparator` function while simultaneously setting `reverse` to `true` results in a potentially different list order than previously. Specifically, when the custom `comparator` fails to sort two objects (compares them as being "equal"), the objects would previously have remained in their original order in the array relative to each other. They will now switch places as the `reverse` will be more accurately respected. To migrate, make sure that your custom `comparator` properly differentiates between all cases that your code base cares about and that "equal" cases are not order dependent. We expect that due to the nature of this change, since the entries are considered "equal", that very few projects will be affected by this change in all but a minor visual manor that still respects the expected operation being performed.
1 parent 8eb925d commit 854c03f

File tree

2 files changed

+20
-2
lines changed

2 files changed

+20
-2
lines changed

src/ng/filter/orderBy.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
*
116116
* @param {boolean=} reverse - If `true`, reverse the sorting order.
117117
* @param {(Function)=} comparator - The comparator function used to determine the relative order of
118-
* value pairs. If omitted, the built-in comparator will be used.
118+
* value pairs. If omitted or finds two entries to be equal, the built-in comparator will be used.
119119
*
120120
* @returns {Array} - The sorted array.
121121
*
@@ -459,6 +459,9 @@
459459
* comparator function. For example, you might need to compare some strings in a locale-sensitive
460460
* way. (When specifying a custom comparator, you also need to pass a value for the `reverse`
461461
* argument - passing `false` retains the default sorting order, i.e. ascending.)
462+
*
463+
* If your comparator fails to differentiate between two items, they will fall back to the built-in
464+
* comparator function, this produces a guarantee of stable sorting.
462465
*
463466
<example name="orderBy-custom-comparator" module="orderByExample4">
464467
<file name="index.html">
@@ -599,7 +602,7 @@ function orderByFilter($parse) {
599602
}
600603
}
601604

602-
return compare(v1.tieBreaker, v2.tieBreaker) * descending;
605+
return (compare(v1.tieBreaker, v2.tieBreaker) || defaultCompare(v1.tieBreaker, v2.tieBreaker)) * descending;
603606
}
604607
};
605608

test/ng/filter/orderBySpec.js

+15
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,21 @@ describe('Filter: orderBy', function() {
457457

458458
expect(orderBy(items, expr, reverse, comparator)).toEqual(sorted);
459459
});
460+
461+
it('should use the default comparator to break ties on a provided comparator', function() {
462+
// Some list that won't be sorted "naturally", i.e. should sort to ['a', 'B', 'c']
463+
var items = ['c', 'a', 'B'];
464+
var expr = null;
465+
function comparator() {
466+
return 0;
467+
}
468+
var reversed = ['B', 'a', 'c'];
469+
var sorted = ['a', 'B', 'c'];
470+
471+
expect(orderBy(items)).toEqual(sorted);
472+
expect(orderBy(items, expr, false, comparator)).toEqual(items);
473+
expect(orderBy(items, expr, true, comparator)).toEqual(reversed);
474+
});
460475
});
461476

462477
describe('(object as `value`)', function() {

0 commit comments

Comments
 (0)