diff --git a/src/ng/filter/orderBy.js b/src/ng/filter/orderBy.js index 22d287f1dc3e..2e9e946d0c75 100644 --- a/src/ng/filter/orderBy.js +++ b/src/ng/filter/orderBy.js @@ -183,6 +183,10 @@ function orderByFilter($parse) { if (sortPredicate.length === 0) { sortPredicate = ['+']; } var predicates = processPredicates(sortPredicate, reverseOrder); + // Add a predicate at the end that evaluates to the element index. This makes the + // sort stable as it works as a tie-breaker when all the input predicates cannot + // distinguish between two elements. + predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1}); // The next three lines are a version of a Swartzian Transform idiom from Perl // (sometimes called the Decorate-Sort-Undecorate idiom) diff --git a/test/ng/filter/orderBySpec.js b/test/ng/filter/orderBySpec.js index 9edec1532cd3..4f371ba80aca 100644 --- a/test/ng/filter/orderBySpec.js +++ b/test/ng/filter/orderBySpec.js @@ -194,6 +194,35 @@ describe('Filter: orderBy', function() { it('should sort mixed array of objects and values in a stable way', function() { expect(orderBy([{foo: 2}, {foo: {}}, {foo: 3}, {foo: 4}], 'foo')).toEqualData([{foo: 2}, {foo: 3}, {foo: 4}, {foo: {}}]); }); + + + it('should perform a stable sort', function() { + expect(orderBy([ + {foo: 2, bar: 1}, {foo: 1, bar: 2}, {foo: 2, bar: 3}, + {foo: 2, bar: 4}, {foo: 1, bar: 5}, {foo: 2, bar: 6}, + {foo: 2, bar: 7}, {foo: 1, bar: 8}, {foo: 2, bar: 9}, + {foo: 1, bar: 10}, {foo: 2, bar: 11}, {foo: 1, bar: 12} + ], 'foo')) + .toEqualData([ + {foo: 1, bar: 2}, {foo: 1, bar: 5}, {foo: 1, bar: 8}, + {foo: 1, bar: 10}, {foo: 1, bar: 12}, {foo: 2, bar: 1}, + {foo: 2, bar: 3}, {foo: 2, bar: 4}, {foo: 2, bar: 6}, + {foo: 2, bar: 7}, {foo: 2, bar: 9}, {foo: 2, bar: 11} + ]); + + expect(orderBy([ + {foo: 2, bar: 1}, {foo: 1, bar: 2}, {foo: 2, bar: 3}, + {foo: 2, bar: 4}, {foo: 1, bar: 5}, {foo: 2, bar: 6}, + {foo: 2, bar: 7}, {foo: 1, bar: 8}, {foo: 2, bar: 9}, + {foo: 1, bar: 10}, {foo: 2, bar: 11}, {foo: 1, bar: 12} + ], 'foo', true)) + .toEqualData([ + {foo: 2, bar: 11}, {foo: 2, bar: 9}, {foo: 2, bar: 7}, + {foo: 2, bar: 6}, {foo: 2, bar: 4}, {foo: 2, bar: 3}, + {foo: 2, bar: 1}, {foo: 1, bar: 12}, {foo: 1, bar: 10}, + {foo: 1, bar: 8}, {foo: 1, bar: 5}, {foo: 1, bar: 2} + ]); + }); });