Skip to content

Commit 45f88ef

Browse files
Craig MacomberCraig Macomber
authored andcommitted
special case finite numbers
1 parent 6807695 commit 45f88ef

File tree

4 files changed

+21
-63
lines changed

4 files changed

+21
-63
lines changed

b+tree.d.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ export declare function compareFiniteNumbers(a: number, b: number): number;
3131
* Compares strings lexically to form a strict partial ordering.
3232
*/
3333
export declare function compareStrings(a: string, b: string): number;
34-
/**
35-
* If a and b are arrays, they are compared using '<' and '>', which may cause unexpected equality, for example [1] will be considered equal to ['1'].
36-
*/
37-
export declare function compareFiniteNumbersOrStringOrArray(a: number | string | (number | string)[], b: number | string | (number | string)[]): number;
3834
/**
3935
* A reasonably fast collection of key-value pairs with a powerful API.
4036
* Largely compatible with the standard Map. BTree is a B+ tree data structure,

b+tree.js

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ var __extends = (this && this.__extends) || (function () {
1313
};
1414
})();
1515
Object.defineProperty(exports, "__esModule", { value: true });
16-
exports.EmptyBTree = exports.compareFiniteNumbersOrStringOrArray = exports.compareStrings = exports.compareFiniteNumbers = exports.defaultComparator = void 0;
16+
exports.EmptyBTree = exports.compareStrings = exports.compareFiniteNumbers = exports.defaultComparator = void 0;
1717
/**
1818
* Compares DefaultComparables to form a strict partial ordering.
1919
*
@@ -24,9 +24,17 @@ exports.EmptyBTree = exports.compareFiniteNumbersOrStringOrArray = exports.compa
2424
* Two objects with equal valueOf compare the same, but compare unequal to primitives that have the same value.
2525
*/
2626
function defaultComparator(a, b) {
27-
// Compare types first.
27+
// Special case finite numbers first for performance.
2828
// 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"),
2929
// leading most comparison functions using that approach to fail to have transitivity.
30+
if (Number.isFinite(a) && Number.isFinite(b)) {
31+
// Does not partially order NaNs or infinite values, but thats fine since they can't reach here.
32+
// This will handle -0 and 0 as equal.
33+
return a - b;
34+
}
35+
// Compare types and order values of different types by type.
36+
// This prevents implicit conversion of strings to numbers from causing invaliding ordering,
37+
// and generally simplifies which cases need to be considered below.
3038
var ta = typeof a;
3139
var tb = typeof b;
3240
if (ta !== tb) {
@@ -82,21 +90,6 @@ function compareStrings(a, b) {
8290
}
8391
exports.compareStrings = compareStrings;
8492
;
85-
/**
86-
* If a and b are arrays, they are compared using '<' and '>', which may cause unexpected equality, for example [1] will be considered equal to ['1'].
87-
*/
88-
function compareFiniteNumbersOrStringOrArray(a, b) {
89-
// Strings can not be ordered relative to numbers using '<' and '>' since no matter the order, the comparison will return false.
90-
var ta = typeof a;
91-
var tb = typeof b;
92-
if (ta !== tb) {
93-
return ta < tb ? -1 : 1;
94-
}
95-
// Use < and > instead of < and === so arrays work correctly.
96-
return a > b ? 1 : a < b ? -1 : 0;
97-
}
98-
exports.compareFiniteNumbersOrStringOrArray = compareFiniteNumbersOrStringOrArray;
99-
;
10093
/**
10194
* A reasonably fast collection of key-value pairs with a powerful API.
10295
* Largely compatible with the standard Map. BTree is a B+ tree data structure,

b+tree.test.ts

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import BTree, {IMap, EmptyBTree, defaultComparator, compareFiniteNumbers, compareFiniteNumbersOrStringOrArray, compareStrings} from './b+tree';
1+
import BTree, {IMap, EmptyBTree, defaultComparator, compareFiniteNumbers, compareStrings} from './b+tree';
22
import SortedArray from './sorted-array';
33
import MersenneTwister from 'mersenne-twister';
44

@@ -71,31 +71,6 @@ describe('compareStrings', () =>
7171
testComparison(compareStrings, [], values, []);
7272
});
7373

74-
describe('compareFiniteNumbersOrStringOrArray', () =>
75-
{
76-
const values = [
77-
'24x',
78-
'0',
79-
'1',
80-
'3',
81-
'String',
82-
'10',
83-
0,
84-
"NaN",
85-
-0,
86-
1,
87-
10,
88-
2,
89-
[],
90-
'[]',
91-
[1],
92-
['1']
93-
];
94-
const sorted = [-10, -1, -0, 0, 1, 2, 10];
95-
testComparison(compareFiniteNumbersOrStringOrArray, sorted, values, [[0, -0], [[1], ['1']]]);
96-
});
97-
98-
9974
/**
10075
* Tests a comparison function, ensuring it produces a strict partial order over the provided values.
10176
* Additionally confirms that the comparison function has the correct definition of equality via expectedDuplicates.

b+tree.ts

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,18 @@ export type DefaultComparable = number | string | (number | string)[] | { valueO
4545
* Two objects with equal valueOf compare the same, but compare unequal to primitives that have the same value.
4646
*/
4747
export function defaultComparator(a: DefaultComparable, b: DefaultComparable): number {
48-
// Compare types first.
48+
// Special case finite numbers first for performance.
4949
// 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"),
5050
// leading most comparison functions using that approach to fail to have transitivity.
51+
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+
return a as number - (b as number);
55+
}
56+
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.
5160
let ta = typeof a;
5261
let tb = typeof b;
5362
if (ta !== tb) {
@@ -86,7 +95,6 @@ export function defaultComparator(a: DefaultComparable, b: DefaultComparable): n
8695
return 0;
8796
};
8897

89-
9098
/**
9199
* Compares finite numbers to form a strict partial ordering.
92100
*
@@ -103,20 +111,6 @@ export function compareStrings(a: string, b:string): number {
103111
return a > b ? 1 : a === b ? 0 : -1;
104112
};
105113

106-
/**
107-
* If a and b are arrays, they are compared using '<' and '>', which may cause unexpected equality, for example [1] will be considered equal to ['1'].
108-
*/
109-
export function compareFiniteNumbersOrStringOrArray(a: number | string | (number| string)[], b: number | string | (number| string)[]): number {
110-
// Strings can not be ordered relative to numbers using '<' and '>' since no matter the order, the comparison will return false.
111-
let ta = typeof a;
112-
let tb = typeof b;
113-
if (ta !== tb) {
114-
return ta < tb ? -1 : 1;
115-
}
116-
// Use < and > instead of < and === so arrays work correctly.
117-
return a > b ? 1 : a < b ? -1 : 0;
118-
};
119-
120114
/**
121115
* A reasonably fast collection of key-value pairs with a powerful API.
122116
* Largely compatible with the standard Map. BTree is a B+ tree data structure,

0 commit comments

Comments
 (0)