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

Commit 1d80464

Browse files
frederikprijckgkalpak
authored andcommitted
feat(orderBy): consider null and undefined greater than other values
Previously, `null` values where sorted using type `string` resulting in a string comparison. `undefined` values where compared to other values by type and were usually considered greater than other values (since their type happens to start with a `u`), but this was coincidental. This commit ensures that `null` and `undefined ` values are explicitly considered greater than other values (with `undefined` > `null`) and will effectively be put at the end of the sorted list (for ascending order sorting). Closes #15294 Closes #16376 BREAKING CHANGE: When using `orderBy` to sort arrays containing `null` values, the `null` values will be considered "greater than" all other values, except for `undefined`. Previously, they were sorted as strings. This will result in different (but more intuitive) sorting order. Before: ```js orderByFilter(['a', undefined, 'o', null, 'z']); //--> 'a', null, 'o', 'z', undefined ``` After: ```js orderByFilter(['a', undefined, 'o', null, 'z']); //--> 'a', 'o', 'z', null, undefined ```
1 parent 40c4990 commit 1d80464

File tree

2 files changed

+30
-11
lines changed

2 files changed

+30
-11
lines changed

src/ng/filter/orderBy.js

+17-8
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
* index: ...
4141
* }
4242
* ```
43+
* **Note:** `null` values use `'null'` as their type.
4344
* 2. The comparator function is used to sort the items, based on the derived values, types and
4445
* indices.
4546
*
@@ -74,11 +75,15 @@
7475
*
7576
* The default, built-in comparator should be sufficient for most usecases. In short, it compares
7677
* numbers numerically, strings alphabetically (and case-insensitively), for objects falls back to
77-
* using their index in the original collection, and sorts values of different types by type.
78+
* using their index in the original collection, sorts values of different types by type and puts
79+
* `undefined` and `null` values at the end of the sorted list.
7880
*
7981
* More specifically, it follows these steps to determine the relative order of items:
8082
*
81-
* 1. If the compared values are of different types, compare the types themselves alphabetically.
83+
* 1. If the compared values are of different types:
84+
* - If one of the values is undefined, consider it "greater than" the other.
85+
* - Else if one of the values is null, consider it "greater than" the other.
86+
* - Else compare the types themselves alphabetically.
8287
* 2. If both values are of type `string`, compare them alphabetically in a case- and
8388
* locale-insensitive way.
8489
* 3. If both values are objects, compare their indices instead.
@@ -89,9 +94,10 @@
8994
*
9095
* **Note:** If you notice numbers not being sorted as expected, make sure they are actually being
9196
* saved as numbers and not strings.
92-
* **Note:** For the purpose of sorting, `null` values are treated as the string `'null'` (i.e.
93-
* `type: 'string'`, `value: 'null'`). This may cause unexpected sort order relative to
94-
* other values.
97+
* **Note:** For the purpose of sorting, `null` and `undefined` are considered "greater than"
98+
* any other value (with undefined "greater than" null). This effectively means that `null`
99+
* and `undefined` values end up at the end of a list sorted in ascending order.
100+
* **Note:** `null` values use `'null'` as their type to be able to distinguish them from objects.
95101
*
96102
* @param {Array|ArrayLike} collection - The collection (array or array-like object) to sort.
97103
* @param {(Function|string|Array.<Function|string>)=} expression - A predicate (or list of
@@ -658,8 +664,7 @@ function orderByFilter($parse) {
658664
function getPredicateValue(value, index) {
659665
var type = typeof value;
660666
if (value === null) {
661-
type = 'string';
662-
value = 'null';
667+
type = 'null';
663668
} else if (type === 'object') {
664669
value = objectValue(value);
665670
}
@@ -690,7 +695,11 @@ function orderByFilter($parse) {
690695
result = value1 < value2 ? -1 : 1;
691696
}
692697
} else {
693-
result = type1 < type2 ? -1 : 1;
698+
result = (type1 === 'undefined') ? 1 :
699+
(type2 === 'undefined') ? -1 :
700+
(type1 === 'null') ? 1 :
701+
(type2 === 'null') ? -1 :
702+
(type1 < type2) ? -1 : 1;
694703
}
695704

696705
return result;

test/ng/filter/orderBySpec.js

+13-3
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,16 @@ describe('Filter: orderBy', function() {
309309

310310
expect(orderBy(items, expr)).toEqual(sorted);
311311
});
312+
313+
it('should consider null and undefined greater than any other value', function() {
314+
var items = [undefined, null, 'z', {}, 999, false];
315+
var expr = null;
316+
var sorted = [false, 999, {}, 'z', null, undefined];
317+
var reversed = [undefined, null, 'z', {}, 999, false];
318+
319+
expect(orderBy(items, expr)).toEqual(sorted);
320+
expect(orderBy(items, expr, true)).toEqual(reversed);
321+
});
312322
});
313323

314324
describe('(custom comparator)', function() {
@@ -376,7 +386,7 @@ describe('Filter: orderBy', function() {
376386
});
377387

378388

379-
it('should treat a value of `null` as `"null"`', function() {
389+
it('should treat a value of `null` as type `"null"`', function() {
380390
var items = [null, null];
381391
var expr = null;
382392
var reverse = null;
@@ -386,8 +396,8 @@ describe('Filter: orderBy', function() {
386396
var arg = comparator.calls.argsFor(0)[0];
387397

388398
expect(arg).toEqual(jasmine.objectContaining({
389-
type: 'string',
390-
value: 'null'
399+
type: 'null',
400+
value: null
391401
}));
392402
});
393403

0 commit comments

Comments
 (0)