@@ -33,7 +33,7 @@ type index = number;
33
33
/**
34
34
* Types that BTree supports by default
35
35
*/
36
- export type DefaultComparable = number | string | ( number | string ) [ ] | { valueOf : ( ) => number | string | ( number | string ) [ ] } ;
36
+ export type DefaultComparable = number | string | Date | boolean | null | undefined | ( number | string ) [ ] | { valueOf : ( ) => number | string | Date | boolean | null | undefined | ( number | string ) [ ] } ;
37
37
38
38
/**
39
39
* Compares DefaultComparables to form a strict partial ordering.
@@ -46,69 +46,67 @@ export type DefaultComparable = number | string | (number | string)[] | { valueO
46
46
*/
47
47
export function defaultComparator ( a : DefaultComparable , b : DefaultComparable ) : number {
48
48
// Special case finite numbers first for performance.
49
- // Note that the trick of using 'a - b' the checking for NaN to detect non numbers values does not work if the strings are numeric (ex: "5"),
50
- // leading most comparison functions using that approach to fail to have transitivity.
49
+ // Note that the trick of using 'a - b' and checking for NaN to detect non-numbers
50
+ // does not work if the strings are numeric (ex: "5"). This would leading most
51
+ // comparison functions using that approach to fail to have transitivity.
51
52
if ( Number . isFinite ( a ) && Number . isFinite ( b ) ) {
52
- // Does not partially order NaNs or infinite values, but thats fine since they can't reach here.
53
- // This will handle -0 and 0 as equal.
54
53
return a as number - ( b as number ) ;
55
54
}
56
55
57
- // Compare types and order values of different types by type.
58
- // This prevents implicit conversion of strings to numbers from causing invaliding ordering,
59
- // and generally simplifies which cases need to be considered below.
56
+ // The default < and > operators are not totally ordered. To allow types to be mixed
57
+ // in a single collection, compare types and order values of different types by type.
60
58
let ta = typeof a ;
61
59
let tb = typeof b ;
62
60
if ( ta !== tb ) {
63
61
return ta < tb ? - 1 : 1 ;
64
62
}
65
63
66
- if ( ta === 'object' ) {
67
- a = a . valueOf ( ) as DefaultComparable ;
68
- b = b . valueOf ( ) as DefaultComparable ;
64
+ if ( ta === 'object' ) {
65
+ // standardized JavaScript bug: null is not an object, but typeof says it is
66
+ if ( a === null )
67
+ return b === null ? 0 : - 1 ;
68
+ else if ( b === null )
69
+ return 1 ;
70
+
71
+ a = a ! . valueOf ( ) as DefaultComparable ;
72
+ b = b ! . valueOf ( ) as DefaultComparable ;
69
73
ta = typeof a ;
70
74
tb = typeof b ;
71
- // Deal with one producing a string, and the other a number
75
+ // Deal with the two valueOf()s producing different types
72
76
if ( ta !== tb ) {
73
77
return ta < tb ? - 1 : 1 ;
74
78
}
75
79
}
76
80
77
- // a and b are now the same type, and either a number, string or array.
78
-
79
- // use Object.is to make NaN compare equal to NaN.
80
- // This treats also -0 as not equal to 0, which is handled separately below.
81
- if ( Object . is ( a , b ) ) return 0 ;
82
-
83
- // All comparisons with NaN return false, so NaNs will pass here.
84
- if ( a < b ) return - 1 ;
85
-
86
- // Since a and b might be arrays, we cannot rely on === or ==, only < and > do something useful for ordering arrays.
87
- // To find if two arrays are equal using comparison operators, both < and > must be checked (even == returns false if not the same object).
88
- if ( a > b ) return 1
81
+ // a and b are now the same type, and will be a number, string or array
82
+ // (which we assume holds numbers or strings), or something unsupported.
83
+ if ( a ! < b ! ) return - 1 ;
84
+ if ( a ! > b ! ) return 1 ;
85
+ if ( a === b ) return 0 ;
89
86
90
87
// Order NaN less than other numbers
91
- if ( Number . isNaN ( a ) ) return - 1 ;
92
- if ( Number . isNaN ( b ) ) return 1 ;
93
-
94
- // Handles 0 and -0 case, as well as equal (but not same object) arrays case.
95
- return 0 ;
88
+ if ( Number . isNaN ( a ) )
89
+ return Number . isNaN ( b ) ? 0 : - 1 ;
90
+ else if ( Number . isNaN ( b ) )
91
+ return 1 ;
92
+ return 0 ; // unreachable?
96
93
} ;
97
94
98
95
/**
99
- * Compares finite numbers to form a strict partial ordering.
96
+ * Compares items using the < and > operators. This function is probably slightly
97
+ * faster than the defaultComparator for Dates and strings, but has not been benchmarked.
98
+ * Unlike defaultComparator, this comparator doesn't support mixed types correctly,
99
+ * i.e. use it with `BTree<string>` or `BTree<Date>` but not `BTree<string|Date>`.
100
100
*
101
- * Handles +/-0 like Map: -0 is equal to +0.
102
- */
103
- export function compareFiniteNumbers ( a : number , b : number ) : number {
104
- return a - b ;
105
- } ;
106
-
107
- /**
108
- * Compares strings lexically to form a strict partial ordering.
101
+ * Note: null compares as less than any number or Date, but in general null is
102
+ * incomparable with strings, and undefined is not comparable with other types
103
+ * using the > and < operators
109
104
*/
110
- export function compareStrings ( a : string , b :string ) : number {
111
- return a > b ? 1 : a === b ? 0 : - 1 ;
105
+ export function simpleComparator ( a : string , b :string ) : number ;
106
+ export function simpleComparator ( a : number | null , b :number | null ) : number ;
107
+ export function simpleComparator ( a : Date | null , b :Date | null ) : number ;
108
+ export function simpleComparator ( a : any , b : any ) : number {
109
+ return a > b ? 1 : a < b ? - 1 : 0 ;
112
110
} ;
113
111
114
112
/**
0 commit comments