@oscarpalmer/atoms 0.173.2 → 0.174.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/array/sort.d.mts +38 -1
- package/dist/array/sort.mjs +51 -17
- package/dist/index.d.mts +38 -1
- package/dist/index.mjs +79 -46
- package/dist/internal/number.mjs +1 -3
- package/dist/internal/value/compare.mjs +20 -20
- package/dist/query.mjs +7 -6
- package/package.json +2 -2
- package/src/array/sort.ts +153 -37
- package/src/internal/number.ts +1 -7
- package/src/internal/value/compare.ts +31 -29
- package/src/query.ts +9 -9
package/dist/array/sort.d.mts
CHANGED
|
@@ -65,7 +65,20 @@ type ComparisonSorter<Item> = (first: Item, second: Item) => number;
|
|
|
65
65
|
* Direction to sort by
|
|
66
66
|
*/
|
|
67
67
|
type SortDirection = 'ascending' | 'descending';
|
|
68
|
-
type Sorter<Item> =
|
|
68
|
+
type Sorter<Item> = {
|
|
69
|
+
/**
|
|
70
|
+
* Sort an array of items
|
|
71
|
+
* @param array Array to sort
|
|
72
|
+
* @returns Sorted array
|
|
73
|
+
*/
|
|
74
|
+
(array: Item[]): Item[];
|
|
75
|
+
/**
|
|
76
|
+
* Is the array sorted?
|
|
77
|
+
* @param array Array to check
|
|
78
|
+
* @returns `true` if sorted, otherwise `false`
|
|
79
|
+
*/
|
|
80
|
+
is(array: Item[]): boolean;
|
|
81
|
+
};
|
|
69
82
|
/**
|
|
70
83
|
* Initialize a sort handler with sorters _(and an optional default direction)_
|
|
71
84
|
* @param sorters Sorters to use for sorting
|
|
@@ -86,6 +99,29 @@ declare function initializeSort<Item>(sorter: ArraySorter<Item>, descending?: bo
|
|
|
86
99
|
* @returns Sort handler
|
|
87
100
|
*/
|
|
88
101
|
declare function initializeSort<Item>(descending?: boolean): Sorter<Item>;
|
|
102
|
+
/**
|
|
103
|
+
* Is the array sorted according to the sorters _(and the optional default direction)_?
|
|
104
|
+
* @param array Array to check
|
|
105
|
+
* @param sorters Sorters to use
|
|
106
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
107
|
+
* @returns `true` if sorted, otherwise `false`
|
|
108
|
+
*/
|
|
109
|
+
declare function isSorted<Item>(array: Item[], sorters: Array<ArraySorter<Item>>, descending?: boolean): boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Is the array sorted according to the sorter _(and the optional default direction)_?
|
|
112
|
+
* @param array Array to check
|
|
113
|
+
* @param sorter Sorter to use
|
|
114
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
115
|
+
* @returns `true` if sorted, otherwise `false`
|
|
116
|
+
*/
|
|
117
|
+
declare function isSorted<Item>(array: Item[], sorter: ArraySorter<Item>, descending?: boolean): boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Is the array sorted?
|
|
120
|
+
* @param array Array to check
|
|
121
|
+
* @param descending Sorted in descending order? _(defaults to `false)_
|
|
122
|
+
* @returns `true` if sorted, otherwise `false`
|
|
123
|
+
*/
|
|
124
|
+
declare function isSorted<Item>(array: Item[], descending?: boolean): boolean;
|
|
89
125
|
/**
|
|
90
126
|
* Sort an array of items, using multiple sorters to sort by specific values
|
|
91
127
|
* @param array Array to sort
|
|
@@ -111,6 +147,7 @@ declare function sort<Item>(array: Item[], sorter: ArraySorter<Item>, descending
|
|
|
111
147
|
declare function sort<Item>(array: Item[], descending?: boolean): Item[];
|
|
112
148
|
declare namespace sort {
|
|
113
149
|
var initialize: typeof initializeSort;
|
|
150
|
+
var is: typeof isSorted;
|
|
114
151
|
}
|
|
115
152
|
declare const SORT_DIRECTION_ASCENDING: SortDirection;
|
|
116
153
|
declare const SORT_DIRECTION_DESCENDING: SortDirection;
|
package/dist/array/sort.mjs
CHANGED
|
@@ -9,6 +9,15 @@ function getComparisonSorter(callback, modifier) {
|
|
|
9
9
|
identifier: String(callback)
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
|
+
function getComparisonValue(first, second, sorters, length) {
|
|
13
|
+
for (let index = 0; index < length; index += 1) {
|
|
14
|
+
const sorter = sorters[index];
|
|
15
|
+
const values = [sorter.get ? sorter.value(first) : first, sorter.get ? sorter.value(second) : second];
|
|
16
|
+
const comparison = (sorter.compare?.complex?.(first, values[0], second, values[1]) ?? sorter.compare?.simple?.(values[0], values[1]) ?? compare(values[0], values[1])) * sorter.modifier;
|
|
17
|
+
if (comparison !== 0) return comparison;
|
|
18
|
+
}
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
12
21
|
function getModifier(first, second) {
|
|
13
22
|
return modifiers[first === true || second === true ? SORT_DIRECTION_DESCENDING : SORT_DIRECTION_ASCENDING];
|
|
14
23
|
}
|
|
@@ -39,6 +48,11 @@ function getSorters(value, modifier) {
|
|
|
39
48
|
const sorter = getSorter(item, modifier);
|
|
40
49
|
if (sorter != null) sorters.push(sorter);
|
|
41
50
|
}
|
|
51
|
+
if (sorters.length === 0) return [{
|
|
52
|
+
modifier,
|
|
53
|
+
get: false,
|
|
54
|
+
identifier: "default"
|
|
55
|
+
}];
|
|
42
56
|
return sorters.filter((value, index, array) => array.findIndex((next) => next.identifier === value.identifier) === index);
|
|
43
57
|
}
|
|
44
58
|
function getValueSorter(value, modifier) {
|
|
@@ -50,30 +64,50 @@ function getValueSorter(value, modifier) {
|
|
|
50
64
|
};
|
|
51
65
|
}
|
|
52
66
|
function initializeSort(first, second) {
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
67
|
+
const sorters = getSorters(first, getModifier(first, second));
|
|
68
|
+
const sorter = (array) => sortArray(array, sorters);
|
|
69
|
+
sorter.is = (array) => isSortedArray(array, sorters);
|
|
70
|
+
return sorter;
|
|
71
|
+
}
|
|
72
|
+
function isSorted(array, first, second) {
|
|
73
|
+
return isSortedArray(array, getSorters(first, getModifier(first, second)));
|
|
74
|
+
}
|
|
75
|
+
function isSortedArray(array, sorters) {
|
|
76
|
+
if (!Array.isArray(array)) return false;
|
|
77
|
+
const { length } = array;
|
|
78
|
+
if (length < 2) return true;
|
|
79
|
+
const sortersLength = sorters.length;
|
|
80
|
+
let offset = 0;
|
|
81
|
+
if (length >= ARRAY_THRESHOLD) {
|
|
82
|
+
offset = Math.round(length / ARRAY_PEEK_PERCENTAGE);
|
|
83
|
+
offset = offset > ARRAY_THRESHOLD ? ARRAY_THRESHOLD : offset;
|
|
84
|
+
for (let index = 0; index < offset; index += 1) {
|
|
85
|
+
const [firstItem, firstOffset] = [array[index], array[index + 1]];
|
|
86
|
+
const [secondItem, secondOffset] = [array[length - index - 2], array[length - index - 1]];
|
|
87
|
+
const [firstComparison, secondComparison] = [getComparisonValue(firstItem, firstOffset, sorters, sortersLength), getComparisonValue(secondItem, secondOffset, sorters, sortersLength)];
|
|
88
|
+
if (firstComparison > 0 || secondComparison > 0) return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const end = length - offset - 1;
|
|
92
|
+
for (let index = offset; index < end; index += 1) {
|
|
93
|
+
const first = array[index];
|
|
94
|
+
const second = array[index + 1];
|
|
95
|
+
if (getComparisonValue(first, second, sorters, sortersLength) > 0) return false;
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
56
98
|
}
|
|
57
99
|
function sort(array, first, second) {
|
|
58
|
-
|
|
59
|
-
return work(array, getSorters(first, modifier), modifier);
|
|
100
|
+
return sortArray(array, getSorters(first, getModifier(first, second)));
|
|
60
101
|
}
|
|
61
|
-
function
|
|
102
|
+
function sortArray(array, sorters) {
|
|
62
103
|
if (!Array.isArray(array)) return [];
|
|
63
|
-
if (array.length < 2) return array;
|
|
64
104
|
const { length } = sorters;
|
|
65
|
-
|
|
66
|
-
return array.sort((first, second) => {
|
|
67
|
-
for (let index = 0; index < length; index += 1) {
|
|
68
|
-
const sorter = sorters[index];
|
|
69
|
-
const values = [sorter.get ? sorter.value(first) : first, sorter.get ? sorter.value(second) : second];
|
|
70
|
-
const comparison = (sorter.compare?.complex?.(first, values[0], second, values[1]) ?? sorter.compare?.simple?.(values[0], values[1]) ?? compare(values[0], values[1])) * sorter.modifier;
|
|
71
|
-
if (comparison !== 0) return comparison;
|
|
72
|
-
}
|
|
73
|
-
return 0;
|
|
74
|
-
});
|
|
105
|
+
return array.length > 1 ? array.sort((first, second) => getComparisonValue(first, second, sorters, length)) : array;
|
|
75
106
|
}
|
|
76
107
|
sort.initialize = initializeSort;
|
|
108
|
+
sort.is = isSorted;
|
|
109
|
+
const ARRAY_PEEK_PERCENTAGE = 10;
|
|
110
|
+
const ARRAY_THRESHOLD = 100;
|
|
77
111
|
const SORT_DIRECTION_ASCENDING = "ascending";
|
|
78
112
|
const SORT_DIRECTION_DESCENDING = "descending";
|
|
79
113
|
const modifiers = {
|
package/dist/index.d.mts
CHANGED
|
@@ -1166,7 +1166,20 @@ type ComparisonSorter<Item> = (first: Item, second: Item) => number;
|
|
|
1166
1166
|
* Direction to sort by
|
|
1167
1167
|
*/
|
|
1168
1168
|
type SortDirection = 'ascending' | 'descending';
|
|
1169
|
-
type Sorter<Item> =
|
|
1169
|
+
type Sorter<Item> = {
|
|
1170
|
+
/**
|
|
1171
|
+
* Sort an array of items
|
|
1172
|
+
* @param array Array to sort
|
|
1173
|
+
* @returns Sorted array
|
|
1174
|
+
*/
|
|
1175
|
+
(array: Item[]): Item[];
|
|
1176
|
+
/**
|
|
1177
|
+
* Is the array sorted?
|
|
1178
|
+
* @param array Array to check
|
|
1179
|
+
* @returns `true` if sorted, otherwise `false`
|
|
1180
|
+
*/
|
|
1181
|
+
is(array: Item[]): boolean;
|
|
1182
|
+
};
|
|
1170
1183
|
/**
|
|
1171
1184
|
* Initialize a sort handler with sorters _(and an optional default direction)_
|
|
1172
1185
|
* @param sorters Sorters to use for sorting
|
|
@@ -1187,6 +1200,29 @@ declare function initializeSort<Item>(sorter: ArraySorter<Item>, descending?: bo
|
|
|
1187
1200
|
* @returns Sort handler
|
|
1188
1201
|
*/
|
|
1189
1202
|
declare function initializeSort<Item>(descending?: boolean): Sorter<Item>;
|
|
1203
|
+
/**
|
|
1204
|
+
* Is the array sorted according to the sorters _(and the optional default direction)_?
|
|
1205
|
+
* @param array Array to check
|
|
1206
|
+
* @param sorters Sorters to use
|
|
1207
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
1208
|
+
* @returns `true` if sorted, otherwise `false`
|
|
1209
|
+
*/
|
|
1210
|
+
declare function isSorted<Item>(array: Item[], sorters: Array<ArraySorter<Item>>, descending?: boolean): boolean;
|
|
1211
|
+
/**
|
|
1212
|
+
* Is the array sorted according to the sorter _(and the optional default direction)_?
|
|
1213
|
+
* @param array Array to check
|
|
1214
|
+
* @param sorter Sorter to use
|
|
1215
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
1216
|
+
* @returns `true` if sorted, otherwise `false`
|
|
1217
|
+
*/
|
|
1218
|
+
declare function isSorted<Item>(array: Item[], sorter: ArraySorter<Item>, descending?: boolean): boolean;
|
|
1219
|
+
/**
|
|
1220
|
+
* Is the array sorted?
|
|
1221
|
+
* @param array Array to check
|
|
1222
|
+
* @param descending Sorted in descending order? _(defaults to `false)_
|
|
1223
|
+
* @returns `true` if sorted, otherwise `false`
|
|
1224
|
+
*/
|
|
1225
|
+
declare function isSorted<Item>(array: Item[], descending?: boolean): boolean;
|
|
1190
1226
|
/**
|
|
1191
1227
|
* Sort an array of items, using multiple sorters to sort by specific values
|
|
1192
1228
|
* @param array Array to sort
|
|
@@ -1212,6 +1248,7 @@ declare function sort<Item>(array: Item[], sorter: ArraySorter<Item>, descending
|
|
|
1212
1248
|
declare function sort<Item>(array: Item[], descending?: boolean): Item[];
|
|
1213
1249
|
declare namespace sort {
|
|
1214
1250
|
var initialize: typeof initializeSort;
|
|
1251
|
+
var is: typeof isSorted;
|
|
1215
1252
|
}
|
|
1216
1253
|
declare const SORT_DIRECTION_ASCENDING: SortDirection;
|
|
1217
1254
|
declare const SORT_DIRECTION_DESCENDING: SortDirection;
|
package/dist/index.mjs
CHANGED
|
@@ -869,26 +869,11 @@ compare.handlers = getCompareHandlers(compare, {
|
|
|
869
869
|
});
|
|
870
870
|
compare.register = registerComparator;
|
|
871
871
|
compare.unregister = unregisterComparator;
|
|
872
|
-
/**
|
|
873
|
-
* Register a custom comparison handler for a class
|
|
874
|
-
* @param constructor Class constructor
|
|
875
|
-
* @param handler Method name or comparison function _(defaults to `compare`)_
|
|
876
|
-
*/
|
|
877
|
-
function registerComparator(constructor, handler) {
|
|
878
|
-
compare.handlers.register(constructor, handler);
|
|
879
|
-
}
|
|
880
|
-
/**
|
|
881
|
-
* Unregister a custom comparison handler for a class
|
|
882
|
-
* @param constructor Class constructor
|
|
883
|
-
*/
|
|
884
|
-
function unregisterComparator(constructor) {
|
|
885
|
-
compare.handlers.unregister(constructor);
|
|
886
|
-
}
|
|
887
872
|
function compareNumbers(first, second) {
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
if (
|
|
891
|
-
return
|
|
873
|
+
if (Object.is(first, second)) return 0;
|
|
874
|
+
if (Number.isNaN(first)) return -1;
|
|
875
|
+
if (Number.isNaN(second)) return 1;
|
|
876
|
+
return first > second ? 1 : -1;
|
|
892
877
|
}
|
|
893
878
|
function compareSymbols(first, second) {
|
|
894
879
|
return getString(first.description ?? first).localeCompare(getString(second.description ?? second));
|
|
@@ -903,9 +888,24 @@ function getComparisonParts(value) {
|
|
|
903
888
|
if (Array.isArray(value)) return value;
|
|
904
889
|
return typeof value === "object" ? [value] : words(getString(value));
|
|
905
890
|
}
|
|
891
|
+
/**
|
|
892
|
+
* Register a custom comparison handler for a class
|
|
893
|
+
* @param constructor Class constructor
|
|
894
|
+
* @param handler Method name or comparison function _(defaults to `compare`)_
|
|
895
|
+
*/
|
|
896
|
+
function registerComparator(constructor, handler) {
|
|
897
|
+
compare.handlers.register(constructor, handler);
|
|
898
|
+
}
|
|
899
|
+
/**
|
|
900
|
+
* Unregister a custom comparison handler for a class
|
|
901
|
+
* @param constructor Class constructor
|
|
902
|
+
*/
|
|
903
|
+
function unregisterComparator(constructor) {
|
|
904
|
+
compare.handlers.unregister(constructor);
|
|
905
|
+
}
|
|
906
906
|
const comparators = {
|
|
907
907
|
bigint: compareNumbers,
|
|
908
|
-
boolean: compareNumbers,
|
|
908
|
+
boolean: (first, second) => compareNumbers(first ? 1 : 0, second ? 1 : 0),
|
|
909
909
|
number: compareNumbers,
|
|
910
910
|
symbol: compareSymbols
|
|
911
911
|
};
|
|
@@ -919,6 +919,15 @@ function getComparisonSorter(callback, modifier) {
|
|
|
919
919
|
identifier: String(callback)
|
|
920
920
|
};
|
|
921
921
|
}
|
|
922
|
+
function getComparisonValue(first, second, sorters, length) {
|
|
923
|
+
for (let index = 0; index < length; index += 1) {
|
|
924
|
+
const sorter = sorters[index];
|
|
925
|
+
const values = [sorter.get ? sorter.value(first) : first, sorter.get ? sorter.value(second) : second];
|
|
926
|
+
const comparison = (sorter.compare?.complex?.(first, values[0], second, values[1]) ?? sorter.compare?.simple?.(values[0], values[1]) ?? compare(values[0], values[1])) * sorter.modifier;
|
|
927
|
+
if (comparison !== 0) return comparison;
|
|
928
|
+
}
|
|
929
|
+
return 0;
|
|
930
|
+
}
|
|
922
931
|
function getModifier(first, second) {
|
|
923
932
|
return modifiers[first === true || second === true ? SORT_DIRECTION_DESCENDING : SORT_DIRECTION_ASCENDING];
|
|
924
933
|
}
|
|
@@ -949,6 +958,11 @@ function getSorters(value, modifier) {
|
|
|
949
958
|
const sorter = getSorter(item, modifier);
|
|
950
959
|
if (sorter != null) sorters.push(sorter);
|
|
951
960
|
}
|
|
961
|
+
if (sorters.length === 0) return [{
|
|
962
|
+
modifier,
|
|
963
|
+
get: false,
|
|
964
|
+
identifier: "default"
|
|
965
|
+
}];
|
|
952
966
|
return sorters.filter((value, index, array) => array.findIndex((next) => next.identifier === value.identifier) === index);
|
|
953
967
|
}
|
|
954
968
|
function getValueSorter(value, modifier) {
|
|
@@ -960,30 +974,50 @@ function getValueSorter(value, modifier) {
|
|
|
960
974
|
};
|
|
961
975
|
}
|
|
962
976
|
function initializeSort(first, second) {
|
|
963
|
-
const
|
|
964
|
-
const
|
|
965
|
-
|
|
977
|
+
const sorters = getSorters(first, getModifier(first, second));
|
|
978
|
+
const sorter = (array) => sortArray(array, sorters);
|
|
979
|
+
sorter.is = (array) => isSortedArray(array, sorters);
|
|
980
|
+
return sorter;
|
|
981
|
+
}
|
|
982
|
+
function isSorted(array, first, second) {
|
|
983
|
+
return isSortedArray(array, getSorters(first, getModifier(first, second)));
|
|
984
|
+
}
|
|
985
|
+
function isSortedArray(array, sorters) {
|
|
986
|
+
if (!Array.isArray(array)) return false;
|
|
987
|
+
const { length } = array;
|
|
988
|
+
if (length < 2) return true;
|
|
989
|
+
const sortersLength = sorters.length;
|
|
990
|
+
let offset = 0;
|
|
991
|
+
if (length >= ARRAY_THRESHOLD$1) {
|
|
992
|
+
offset = Math.round(length / ARRAY_PEEK_PERCENTAGE$1);
|
|
993
|
+
offset = offset > ARRAY_THRESHOLD$1 ? ARRAY_THRESHOLD$1 : offset;
|
|
994
|
+
for (let index = 0; index < offset; index += 1) {
|
|
995
|
+
const [firstItem, firstOffset] = [array[index], array[index + 1]];
|
|
996
|
+
const [secondItem, secondOffset] = [array[length - index - 2], array[length - index - 1]];
|
|
997
|
+
const [firstComparison, secondComparison] = [getComparisonValue(firstItem, firstOffset, sorters, sortersLength), getComparisonValue(secondItem, secondOffset, sorters, sortersLength)];
|
|
998
|
+
if (firstComparison > 0 || secondComparison > 0) return false;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
const end = length - offset - 1;
|
|
1002
|
+
for (let index = offset; index < end; index += 1) {
|
|
1003
|
+
const first = array[index];
|
|
1004
|
+
const second = array[index + 1];
|
|
1005
|
+
if (getComparisonValue(first, second, sorters, sortersLength) > 0) return false;
|
|
1006
|
+
}
|
|
1007
|
+
return true;
|
|
966
1008
|
}
|
|
967
1009
|
function sort(array, first, second) {
|
|
968
|
-
|
|
969
|
-
return work$1(array, getSorters(first, modifier), modifier);
|
|
1010
|
+
return sortArray(array, getSorters(first, getModifier(first, second)));
|
|
970
1011
|
}
|
|
971
|
-
function
|
|
1012
|
+
function sortArray(array, sorters) {
|
|
972
1013
|
if (!Array.isArray(array)) return [];
|
|
973
|
-
if (array.length < 2) return array;
|
|
974
1014
|
const { length } = sorters;
|
|
975
|
-
|
|
976
|
-
return array.sort((first, second) => {
|
|
977
|
-
for (let index = 0; index < length; index += 1) {
|
|
978
|
-
const sorter = sorters[index];
|
|
979
|
-
const values = [sorter.get ? sorter.value(first) : first, sorter.get ? sorter.value(second) : second];
|
|
980
|
-
const comparison = (sorter.compare?.complex?.(first, values[0], second, values[1]) ?? sorter.compare?.simple?.(values[0], values[1]) ?? compare(values[0], values[1])) * sorter.modifier;
|
|
981
|
-
if (comparison !== 0) return comparison;
|
|
982
|
-
}
|
|
983
|
-
return 0;
|
|
984
|
-
});
|
|
1015
|
+
return array.length > 1 ? array.sort((first, second) => getComparisonValue(first, second, sorters, length)) : array;
|
|
985
1016
|
}
|
|
986
1017
|
sort.initialize = initializeSort;
|
|
1018
|
+
sort.is = isSorted;
|
|
1019
|
+
const ARRAY_PEEK_PERCENTAGE$1 = 10;
|
|
1020
|
+
const ARRAY_THRESHOLD$1 = 100;
|
|
987
1021
|
const SORT_DIRECTION_ASCENDING = "ascending";
|
|
988
1022
|
const SORT_DIRECTION_DESCENDING = "descending";
|
|
989
1023
|
const modifiers = {
|
|
@@ -1198,12 +1232,10 @@ function getNumber(value) {
|
|
|
1198
1232
|
if (EXPRESSION_ZEROISH.test(parsed)) return 0;
|
|
1199
1233
|
const isBinary = EXPRESSION_BINARY.test(trimmed);
|
|
1200
1234
|
if (isBinary || EXPRESSION_OCTAL.test(trimmed)) return Number.parseInt(trimmed.slice(2), isBinary ? 2 : OCTAL_VALUE);
|
|
1201
|
-
return Number(
|
|
1235
|
+
return Number(trimmed);
|
|
1202
1236
|
}
|
|
1203
1237
|
const EXPRESSION_BINARY = /^0b[01]+$/i;
|
|
1204
|
-
const EXPRESSION_HEX = /^0x[0-9a-f]+$/i;
|
|
1205
1238
|
const EXPRESSION_OCTAL = /^0o[0-7]+$/i;
|
|
1206
|
-
const EXPRESSION_UNDERSCORE = /_/g;
|
|
1207
1239
|
const EXPRESSION_ZEROISH = /^\s*0+\s*$/;
|
|
1208
1240
|
const OCTAL_VALUE = 8;
|
|
1209
1241
|
//#endregion
|
|
@@ -4058,11 +4090,11 @@ async function resultPromises(items, signal) {
|
|
|
4058
4090
|
*/
|
|
4059
4091
|
function fromQuery(query) {
|
|
4060
4092
|
if (typeof query !== "string" || query.trim().length === 0) return {};
|
|
4061
|
-
const parts = query.split(
|
|
4093
|
+
const parts = query.split(AMPERSAND);
|
|
4062
4094
|
const { length } = parts;
|
|
4063
4095
|
const parameters = {};
|
|
4064
4096
|
for (let index = 0; index < length; index += 1) {
|
|
4065
|
-
const decoded = parts[index].split(
|
|
4097
|
+
const decoded = parts[index].split(EQUAL).map(tryDecode);
|
|
4066
4098
|
const key = decoded[0].replace(EXPRESSION_ARRAY_SUFFIX, "");
|
|
4067
4099
|
if (!ignoreKey(key)) setQueryValue(parameters, key, decoded[1]);
|
|
4068
4100
|
}
|
|
@@ -4085,7 +4117,6 @@ function getParts(value, fromArray, prefix) {
|
|
|
4085
4117
|
}
|
|
4086
4118
|
function getQueryValue(value) {
|
|
4087
4119
|
if (EXPRESSION_BOOLEAN.test(value)) return value === TRUE;
|
|
4088
|
-
if (EXPRESSION_NOPARSE.test(value)) return value;
|
|
4089
4120
|
const asNumber = getNumber(value);
|
|
4090
4121
|
if (!Number.isNaN(asNumber)) return asNumber;
|
|
4091
4122
|
const parsed = Date.parse(value);
|
|
@@ -4097,7 +4128,7 @@ function isDecodable(value) {
|
|
|
4097
4128
|
return TYPES.has(typeof value);
|
|
4098
4129
|
}
|
|
4099
4130
|
function setQueryValue(parameters, key, value) {
|
|
4100
|
-
if (
|
|
4131
|
+
if (EXPRESSION_DOT.test(key)) setValue(parameters, key, getQueryValue(value));
|
|
4101
4132
|
else if (key in parameters) {
|
|
4102
4133
|
if (!Array.isArray(parameters[key])) parameters[key] = [parameters[key]];
|
|
4103
4134
|
parameters[key].push(getQueryValue(value));
|
|
@@ -4109,11 +4140,13 @@ function setQueryValue(parameters, key, value) {
|
|
|
4109
4140
|
* @returns Query string representation of the object
|
|
4110
4141
|
*/
|
|
4111
4142
|
function toQuery(parameters) {
|
|
4112
|
-
return isPlainObject(parameters) ? join(getParts(parameters, false).filter((part) => part.length > 0),
|
|
4143
|
+
return isPlainObject(parameters) ? join(getParts(parameters, false).filter((part) => part.length > 0), AMPERSAND) : "";
|
|
4113
4144
|
}
|
|
4145
|
+
const AMPERSAND = "&";
|
|
4146
|
+
const EQUAL = "=";
|
|
4114
4147
|
const EXPRESSION_ARRAY_SUFFIX = /\[\]$/;
|
|
4115
4148
|
const EXPRESSION_BOOLEAN = /^(false|true)$/;
|
|
4116
|
-
const
|
|
4149
|
+
const EXPRESSION_DOT = /\./g;
|
|
4117
4150
|
const TRUE = "true";
|
|
4118
4151
|
const TYPES = new Set([
|
|
4119
4152
|
"boolean",
|
package/dist/internal/number.mjs
CHANGED
|
@@ -51,12 +51,10 @@ function getNumber(value) {
|
|
|
51
51
|
if (EXPRESSION_ZEROISH.test(parsed)) return 0;
|
|
52
52
|
const isBinary = EXPRESSION_BINARY.test(trimmed);
|
|
53
53
|
if (isBinary || EXPRESSION_OCTAL.test(trimmed)) return Number.parseInt(trimmed.slice(2), isBinary ? 2 : OCTAL_VALUE);
|
|
54
|
-
return Number(
|
|
54
|
+
return Number(trimmed);
|
|
55
55
|
}
|
|
56
56
|
const EXPRESSION_BINARY = /^0b[01]+$/i;
|
|
57
|
-
const EXPRESSION_HEX = /^0x[0-9a-f]+$/i;
|
|
58
57
|
const EXPRESSION_OCTAL = /^0o[0-7]+$/i;
|
|
59
|
-
const EXPRESSION_UNDERSCORE = /_/g;
|
|
60
58
|
const EXPRESSION_ZEROISH = /^\s*0+\s*$/;
|
|
61
59
|
const OCTAL_VALUE = 8;
|
|
62
60
|
//#endregion
|
|
@@ -39,26 +39,11 @@ compare.handlers = getCompareHandlers(compare, {
|
|
|
39
39
|
});
|
|
40
40
|
compare.register = registerComparator;
|
|
41
41
|
compare.unregister = unregisterComparator;
|
|
42
|
-
/**
|
|
43
|
-
* Register a custom comparison handler for a class
|
|
44
|
-
* @param constructor Class constructor
|
|
45
|
-
* @param handler Method name or comparison function _(defaults to `compare`)_
|
|
46
|
-
*/
|
|
47
|
-
function registerComparator(constructor, handler) {
|
|
48
|
-
compare.handlers.register(constructor, handler);
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Unregister a custom comparison handler for a class
|
|
52
|
-
* @param constructor Class constructor
|
|
53
|
-
*/
|
|
54
|
-
function unregisterComparator(constructor) {
|
|
55
|
-
compare.handlers.unregister(constructor);
|
|
56
|
-
}
|
|
57
42
|
function compareNumbers(first, second) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (
|
|
61
|
-
return
|
|
43
|
+
if (Object.is(first, second)) return 0;
|
|
44
|
+
if (Number.isNaN(first)) return -1;
|
|
45
|
+
if (Number.isNaN(second)) return 1;
|
|
46
|
+
return first > second ? 1 : -1;
|
|
62
47
|
}
|
|
63
48
|
function compareSymbols(first, second) {
|
|
64
49
|
return getString(first.description ?? first).localeCompare(getString(second.description ?? second));
|
|
@@ -73,9 +58,24 @@ function getComparisonParts(value) {
|
|
|
73
58
|
if (Array.isArray(value)) return value;
|
|
74
59
|
return typeof value === "object" ? [value] : words(getString(value));
|
|
75
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Register a custom comparison handler for a class
|
|
63
|
+
* @param constructor Class constructor
|
|
64
|
+
* @param handler Method name or comparison function _(defaults to `compare`)_
|
|
65
|
+
*/
|
|
66
|
+
function registerComparator(constructor, handler) {
|
|
67
|
+
compare.handlers.register(constructor, handler);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Unregister a custom comparison handler for a class
|
|
71
|
+
* @param constructor Class constructor
|
|
72
|
+
*/
|
|
73
|
+
function unregisterComparator(constructor) {
|
|
74
|
+
compare.handlers.unregister(constructor);
|
|
75
|
+
}
|
|
76
76
|
const comparators = {
|
|
77
77
|
bigint: compareNumbers,
|
|
78
|
-
boolean: compareNumbers,
|
|
78
|
+
boolean: (first, second) => compareNumbers(first ? 1 : 0, second ? 1 : 0),
|
|
79
79
|
number: compareNumbers,
|
|
80
80
|
symbol: compareSymbols
|
|
81
81
|
};
|
package/dist/query.mjs
CHANGED
|
@@ -10,11 +10,11 @@ import { setValue } from "./internal/value/set.mjs";
|
|
|
10
10
|
*/
|
|
11
11
|
function fromQuery(query) {
|
|
12
12
|
if (typeof query !== "string" || query.trim().length === 0) return {};
|
|
13
|
-
const parts = query.split(
|
|
13
|
+
const parts = query.split(AMPERSAND);
|
|
14
14
|
const { length } = parts;
|
|
15
15
|
const parameters = {};
|
|
16
16
|
for (let index = 0; index < length; index += 1) {
|
|
17
|
-
const decoded = parts[index].split(
|
|
17
|
+
const decoded = parts[index].split(EQUAL).map(tryDecode);
|
|
18
18
|
const key = decoded[0].replace(EXPRESSION_ARRAY_SUFFIX, "");
|
|
19
19
|
if (!ignoreKey(key)) setQueryValue(parameters, key, decoded[1]);
|
|
20
20
|
}
|
|
@@ -37,7 +37,6 @@ function getParts(value, fromArray, prefix) {
|
|
|
37
37
|
}
|
|
38
38
|
function getQueryValue(value) {
|
|
39
39
|
if (EXPRESSION_BOOLEAN.test(value)) return value === TRUE;
|
|
40
|
-
if (EXPRESSION_NOPARSE.test(value)) return value;
|
|
41
40
|
const asNumber = getNumber(value);
|
|
42
41
|
if (!Number.isNaN(asNumber)) return asNumber;
|
|
43
42
|
const parsed = Date.parse(value);
|
|
@@ -49,7 +48,7 @@ function isDecodable(value) {
|
|
|
49
48
|
return TYPES.has(typeof value);
|
|
50
49
|
}
|
|
51
50
|
function setQueryValue(parameters, key, value) {
|
|
52
|
-
if (
|
|
51
|
+
if (EXPRESSION_DOT.test(key)) setValue(parameters, key, getQueryValue(value));
|
|
53
52
|
else if (key in parameters) {
|
|
54
53
|
if (!Array.isArray(parameters[key])) parameters[key] = [parameters[key]];
|
|
55
54
|
parameters[key].push(getQueryValue(value));
|
|
@@ -61,11 +60,13 @@ function setQueryValue(parameters, key, value) {
|
|
|
61
60
|
* @returns Query string representation of the object
|
|
62
61
|
*/
|
|
63
62
|
function toQuery(parameters) {
|
|
64
|
-
return isPlainObject(parameters) ? join(getParts(parameters, false).filter((part) => part.length > 0),
|
|
63
|
+
return isPlainObject(parameters) ? join(getParts(parameters, false).filter((part) => part.length > 0), AMPERSAND) : "";
|
|
65
64
|
}
|
|
65
|
+
const AMPERSAND = "&";
|
|
66
|
+
const EQUAL = "=";
|
|
66
67
|
const EXPRESSION_ARRAY_SUFFIX = /\[\]$/;
|
|
67
68
|
const EXPRESSION_BOOLEAN = /^(false|true)$/;
|
|
68
|
-
const
|
|
69
|
+
const EXPRESSION_DOT = /\./g;
|
|
69
70
|
const TRUE = "true";
|
|
70
71
|
const TYPES = new Set([
|
|
71
72
|
"boolean",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oscarpalmer/atoms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.174.0",
|
|
4
4
|
"description": "Atomic utilities for making your JavaScript better.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"helper",
|
|
@@ -230,7 +230,7 @@
|
|
|
230
230
|
},
|
|
231
231
|
"devDependencies": {
|
|
232
232
|
"@oxlint/plugins": "^1.59",
|
|
233
|
-
"@types/node": "^25.
|
|
233
|
+
"@types/node": "^25.6",
|
|
234
234
|
"@vitest/coverage-istanbul": "^4.1",
|
|
235
235
|
"eslint": "^10.2",
|
|
236
236
|
"jsdom": "^29.0",
|
package/src/array/sort.ts
CHANGED
|
@@ -104,7 +104,21 @@ type InternalSorterCompare = {
|
|
|
104
104
|
*/
|
|
105
105
|
export type SortDirection = 'ascending' | 'descending';
|
|
106
106
|
|
|
107
|
-
export type Sorter<Item> =
|
|
107
|
+
export type Sorter<Item> = {
|
|
108
|
+
/**
|
|
109
|
+
* Sort an array of items
|
|
110
|
+
* @param array Array to sort
|
|
111
|
+
* @returns Sorted array
|
|
112
|
+
*/
|
|
113
|
+
(array: Item[]): Item[];
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Is the array sorted?
|
|
117
|
+
* @param array Array to check
|
|
118
|
+
* @returns `true` if sorted, otherwise `false`
|
|
119
|
+
*/
|
|
120
|
+
is(array: Item[]): boolean;
|
|
121
|
+
};
|
|
108
122
|
|
|
109
123
|
// #endregion
|
|
110
124
|
|
|
@@ -121,6 +135,33 @@ function getComparisonSorter(callback: Function, modifier: number): InternalSort
|
|
|
121
135
|
};
|
|
122
136
|
}
|
|
123
137
|
|
|
138
|
+
function getComparisonValue(
|
|
139
|
+
first: unknown,
|
|
140
|
+
second: unknown,
|
|
141
|
+
sorters: InternalSorter[],
|
|
142
|
+
length: number,
|
|
143
|
+
): number {
|
|
144
|
+
for (let index = 0; index < length; index += 1) {
|
|
145
|
+
const sorter = sorters[index];
|
|
146
|
+
|
|
147
|
+
const values = [
|
|
148
|
+
sorter.get ? sorter.value!(first as PlainObject) : first,
|
|
149
|
+
sorter.get ? sorter.value!(second as PlainObject) : second,
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
const comparison =
|
|
153
|
+
(sorter.compare?.complex?.(first, values[0], second, values[1]) ??
|
|
154
|
+
sorter.compare?.simple?.(values[0], values[1]) ??
|
|
155
|
+
compare(values[0], values[1])) * sorter.modifier;
|
|
156
|
+
|
|
157
|
+
if (comparison !== 0) {
|
|
158
|
+
return comparison;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return 0;
|
|
163
|
+
}
|
|
164
|
+
|
|
124
165
|
function getModifier(first: unknown, second: unknown): number {
|
|
125
166
|
const direction =
|
|
126
167
|
first === true || second === true ? SORT_DIRECTION_DESCENDING : SORT_DIRECTION_ASCENDING;
|
|
@@ -184,6 +225,16 @@ function getSorters(value: unknown, modifier: number): InternalSorter[] {
|
|
|
184
225
|
}
|
|
185
226
|
}
|
|
186
227
|
|
|
228
|
+
if (sorters.length === 0) {
|
|
229
|
+
return [
|
|
230
|
+
{
|
|
231
|
+
modifier,
|
|
232
|
+
get: false,
|
|
233
|
+
identifier: 'default',
|
|
234
|
+
},
|
|
235
|
+
];
|
|
236
|
+
}
|
|
237
|
+
|
|
187
238
|
return sorters.filter(
|
|
188
239
|
(value, index, array) =>
|
|
189
240
|
array.findIndex(next => next.identifier === value.identifier) === index,
|
|
@@ -226,10 +277,97 @@ function initializeSort<Item>(sorter: ArraySorter<Item>, descending?: boolean):
|
|
|
226
277
|
function initializeSort<Item>(descending?: boolean): Sorter<Item>;
|
|
227
278
|
|
|
228
279
|
function initializeSort(first?: unknown, second?: unknown): Sorter<unknown> {
|
|
229
|
-
const
|
|
230
|
-
|
|
280
|
+
const sorters = getSorters(first, getModifier(first, second));
|
|
281
|
+
|
|
282
|
+
const sorter = (array: unknown[]) => sortArray(array, sorters);
|
|
283
|
+
|
|
284
|
+
sorter.is = (array: unknown[]) => isSortedArray(array, sorters);
|
|
231
285
|
|
|
232
|
-
return
|
|
286
|
+
return sorter as unknown as Sorter<unknown>;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Is the array sorted according to the sorters _(and the optional default direction)_?
|
|
291
|
+
* @param array Array to check
|
|
292
|
+
* @param sorters Sorters to use
|
|
293
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
294
|
+
* @returns `true` if sorted, otherwise `false`
|
|
295
|
+
*/
|
|
296
|
+
function isSorted<Item>(
|
|
297
|
+
array: Item[],
|
|
298
|
+
sorters: Array<ArraySorter<Item>>,
|
|
299
|
+
descending?: boolean,
|
|
300
|
+
): boolean;
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Is the array sorted according to the sorter _(and the optional default direction)_?
|
|
304
|
+
* @param array Array to check
|
|
305
|
+
* @param sorter Sorter to use
|
|
306
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
307
|
+
* @returns `true` if sorted, otherwise `false`
|
|
308
|
+
*/
|
|
309
|
+
function isSorted<Item>(array: Item[], sorter: ArraySorter<Item>, descending?: boolean): boolean;
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Is the array sorted?
|
|
313
|
+
* @param array Array to check
|
|
314
|
+
* @param descending Sorted in descending order? _(defaults to `false)_
|
|
315
|
+
* @returns `true` if sorted, otherwise `false`
|
|
316
|
+
*/
|
|
317
|
+
function isSorted<Item>(array: Item[], descending?: boolean): boolean;
|
|
318
|
+
|
|
319
|
+
function isSorted(array: unknown[], first?: unknown, second?: unknown): boolean {
|
|
320
|
+
return isSortedArray(array, getSorters(first, getModifier(first, second)));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function isSortedArray(array: unknown[], sorters: InternalSorter[]): boolean {
|
|
324
|
+
if (!Array.isArray(array)) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const {length} = array;
|
|
329
|
+
|
|
330
|
+
if (length < 2) {
|
|
331
|
+
return true;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const sortersLength = sorters.length;
|
|
335
|
+
|
|
336
|
+
let offset = 0;
|
|
337
|
+
|
|
338
|
+
if (length >= ARRAY_THRESHOLD) {
|
|
339
|
+
offset = Math.round(length / ARRAY_PEEK_PERCENTAGE);
|
|
340
|
+
offset = offset > ARRAY_THRESHOLD ? ARRAY_THRESHOLD : offset;
|
|
341
|
+
|
|
342
|
+
for (let index = 0; index < offset; index += 1) {
|
|
343
|
+
const [firstItem, firstOffset] = [array[index], array[index + 1]];
|
|
344
|
+
const [secondItem, secondOffset] = [array[length - index - 2], array[length - index - 1]];
|
|
345
|
+
|
|
346
|
+
const [firstComparison, secondComparison] = [
|
|
347
|
+
getComparisonValue(firstItem, firstOffset, sorters, sortersLength),
|
|
348
|
+
getComparisonValue(secondItem, secondOffset, sorters, sortersLength),
|
|
349
|
+
];
|
|
350
|
+
|
|
351
|
+
if (firstComparison > 0 || secondComparison > 0) {
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const end = length - offset - 1;
|
|
358
|
+
|
|
359
|
+
for (let index = offset; index < end; index += 1) {
|
|
360
|
+
const first = array[index];
|
|
361
|
+
const second = array[index + 1];
|
|
362
|
+
|
|
363
|
+
const comparison = getComparisonValue(first, second, sorters, sortersLength);
|
|
364
|
+
|
|
365
|
+
if (comparison > 0) {
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return true;
|
|
233
371
|
}
|
|
234
372
|
|
|
235
373
|
/**
|
|
@@ -263,55 +401,33 @@ export function sort<Item>(array: Item[], sorter: ArraySorter<Item>, descending?
|
|
|
263
401
|
export function sort<Item>(array: Item[], descending?: boolean): Item[];
|
|
264
402
|
|
|
265
403
|
export function sort(array: unknown[], first?: unknown, second?: unknown): unknown[] {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
return work(array, getSorters(first, modifier), modifier);
|
|
404
|
+
return sortArray(array, getSorters(first, getModifier(first, second)));
|
|
269
405
|
}
|
|
270
406
|
|
|
271
|
-
function
|
|
407
|
+
function sortArray(array: unknown[], sorters: InternalSorter[]): unknown[] {
|
|
272
408
|
if (!Array.isArray(array)) {
|
|
273
409
|
return [];
|
|
274
410
|
}
|
|
275
411
|
|
|
276
|
-
if (array.length < 2) {
|
|
277
|
-
return array;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
412
|
const {length} = sorters;
|
|
281
413
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
return array.sort((first, second) => {
|
|
287
|
-
for (let index = 0; index < length; index += 1) {
|
|
288
|
-
const sorter = sorters[index];
|
|
289
|
-
|
|
290
|
-
const values = [
|
|
291
|
-
sorter.get ? sorter.value!(first as PlainObject) : first,
|
|
292
|
-
sorter.get ? sorter.value!(second as PlainObject) : second,
|
|
293
|
-
];
|
|
294
|
-
|
|
295
|
-
const comparison =
|
|
296
|
-
(sorter.compare?.complex?.(first, values[0], second, values[1]) ??
|
|
297
|
-
sorter.compare?.simple?.(values[0], values[1]) ??
|
|
298
|
-
compare(values[0], values[1])) * sorter.modifier;
|
|
299
|
-
|
|
300
|
-
if (comparison !== 0) {
|
|
301
|
-
return comparison;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
return 0;
|
|
306
|
-
});
|
|
414
|
+
return array.length > 1
|
|
415
|
+
? array.sort((first, second) => getComparisonValue(first, second, sorters, length))
|
|
416
|
+
: array;
|
|
307
417
|
}
|
|
308
418
|
|
|
309
419
|
sort.initialize = initializeSort;
|
|
310
420
|
|
|
421
|
+
sort.is = isSorted;
|
|
422
|
+
|
|
311
423
|
// #endregion
|
|
312
424
|
|
|
313
425
|
// #region Variables
|
|
314
426
|
|
|
427
|
+
const ARRAY_PEEK_PERCENTAGE = 10;
|
|
428
|
+
|
|
429
|
+
const ARRAY_THRESHOLD = 100;
|
|
430
|
+
|
|
315
431
|
export const SORT_DIRECTION_ASCENDING: SortDirection = 'ascending';
|
|
316
432
|
|
|
317
433
|
export const SORT_DIRECTION_DESCENDING: SortDirection = 'descending';
|
package/src/internal/number.ts
CHANGED
|
@@ -94,9 +94,7 @@ export function getNumber(value: unknown): number {
|
|
|
94
94
|
return Number.parseInt(trimmed.slice(2), isBinary ? 2 : OCTAL_VALUE);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
return Number(
|
|
98
|
-
EXPRESSION_HEX.test(trimmed) ? trimmed : trimmed.replace(EXPRESSION_UNDERSCORE, ''),
|
|
99
|
-
);
|
|
97
|
+
return Number(trimmed);
|
|
100
98
|
}
|
|
101
99
|
|
|
102
100
|
// #endregion
|
|
@@ -105,12 +103,8 @@ export function getNumber(value: unknown): number {
|
|
|
105
103
|
|
|
106
104
|
const EXPRESSION_BINARY = /^0b[01]+$/i;
|
|
107
105
|
|
|
108
|
-
const EXPRESSION_HEX = /^0x[0-9a-f]+$/i;
|
|
109
|
-
|
|
110
106
|
const EXPRESSION_OCTAL = /^0o[0-7]+$/i;
|
|
111
107
|
|
|
112
|
-
const EXPRESSION_UNDERSCORE = /_/g;
|
|
113
|
-
|
|
114
108
|
const EXPRESSION_ZEROISH = /^\s*0+\s*$/;
|
|
115
109
|
|
|
116
110
|
const OCTAL_VALUE = 8;
|
|
@@ -80,38 +80,20 @@ compare.register = registerComparator;
|
|
|
80
80
|
|
|
81
81
|
compare.unregister = unregisterComparator;
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
*/
|
|
88
|
-
function registerComparator<Instance>(
|
|
89
|
-
constructor: Constructor<Instance>,
|
|
90
|
-
handler?: string | ((first: Instance, second: Instance) => number),
|
|
91
|
-
): void {
|
|
92
|
-
compare.handlers.register(constructor, handler);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Unregister a custom comparison handler for a class
|
|
97
|
-
* @param constructor Class constructor
|
|
98
|
-
*/
|
|
99
|
-
function unregisterComparator<Instance>(constructor: Constructor<Instance>): void {
|
|
100
|
-
compare.handlers.unregister(constructor);
|
|
101
|
-
}
|
|
83
|
+
function compareNumbers(first: bigint | number, second: bigint | number): number {
|
|
84
|
+
if (Object.is(first, second)) {
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
102
87
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
): number {
|
|
107
|
-
const firstNumber = Number(first);
|
|
108
|
-
const secondNumber = Number(second);
|
|
88
|
+
if (Number.isNaN(first)) {
|
|
89
|
+
return -1;
|
|
90
|
+
}
|
|
109
91
|
|
|
110
|
-
if (
|
|
111
|
-
return
|
|
92
|
+
if (Number.isNaN(second)) {
|
|
93
|
+
return 1;
|
|
112
94
|
}
|
|
113
95
|
|
|
114
|
-
return
|
|
96
|
+
return first > second ? 1 : -1;
|
|
115
97
|
}
|
|
116
98
|
|
|
117
99
|
function compareSymbols(first: symbol, second: symbol): number {
|
|
@@ -147,13 +129,33 @@ function getComparisonParts(value: unknown): unknown[] {
|
|
|
147
129
|
return typeof value === 'object' ? [value] : words(getString(value));
|
|
148
130
|
}
|
|
149
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Register a custom comparison handler for a class
|
|
134
|
+
* @param constructor Class constructor
|
|
135
|
+
* @param handler Method name or comparison function _(defaults to `compare`)_
|
|
136
|
+
*/
|
|
137
|
+
function registerComparator<Instance>(
|
|
138
|
+
constructor: Constructor<Instance>,
|
|
139
|
+
handler?: string | ((first: Instance, second: Instance) => number),
|
|
140
|
+
): void {
|
|
141
|
+
compare.handlers.register(constructor, handler);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Unregister a custom comparison handler for a class
|
|
146
|
+
* @param constructor Class constructor
|
|
147
|
+
*/
|
|
148
|
+
function unregisterComparator<Instance>(constructor: Constructor<Instance>): void {
|
|
149
|
+
compare.handlers.unregister(constructor);
|
|
150
|
+
}
|
|
151
|
+
|
|
150
152
|
// #endregion
|
|
151
153
|
|
|
152
154
|
// #region Variables
|
|
153
155
|
|
|
154
156
|
const comparators: Record<string, Comparator> = {
|
|
155
157
|
bigint: compareNumbers,
|
|
156
|
-
boolean: compareNumbers,
|
|
158
|
+
boolean: (first, second) => compareNumbers(first ? 1 : 0, second ? 1 : 0),
|
|
157
159
|
number: compareNumbers,
|
|
158
160
|
symbol: compareSymbols,
|
|
159
161
|
};
|
package/src/query.ts
CHANGED
|
@@ -16,13 +16,13 @@ export function fromQuery(query: string): PlainObject {
|
|
|
16
16
|
return {};
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const parts = query.split(
|
|
19
|
+
const parts = query.split(AMPERSAND);
|
|
20
20
|
const {length} = parts;
|
|
21
21
|
|
|
22
22
|
const parameters: PlainObject = {};
|
|
23
23
|
|
|
24
24
|
for (let index = 0; index < length; index += 1) {
|
|
25
|
-
const decoded = parts[index].split(
|
|
25
|
+
const decoded = parts[index].split(EQUAL).map(tryDecode);
|
|
26
26
|
const key = decoded[0].replace(EXPRESSION_ARRAY_SUFFIX, '');
|
|
27
27
|
|
|
28
28
|
if (!ignoreKey(key)) {
|
|
@@ -64,10 +64,6 @@ function getQueryValue(value: string): unknown {
|
|
|
64
64
|
return value === TRUE;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
if (EXPRESSION_NOPARSE.test(value)) {
|
|
68
|
-
return value;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
67
|
const asNumber = getNumber(value);
|
|
72
68
|
|
|
73
69
|
if (!Number.isNaN(asNumber)) {
|
|
@@ -90,7 +86,7 @@ function isDecodable(value: unknown): value is boolean | number | string {
|
|
|
90
86
|
}
|
|
91
87
|
|
|
92
88
|
function setQueryValue(parameters: PlainObject, key: string, value: string): void {
|
|
93
|
-
if (
|
|
89
|
+
if (EXPRESSION_DOT.test(key)) {
|
|
94
90
|
setValue(parameters, key, getQueryValue(value));
|
|
95
91
|
} else {
|
|
96
92
|
if (key in parameters) {
|
|
@@ -114,7 +110,7 @@ export function toQuery(parameters: PlainObject): string {
|
|
|
114
110
|
return isPlainObject(parameters)
|
|
115
111
|
? join(
|
|
116
112
|
getParts(parameters, false).filter(part => part.length > 0),
|
|
117
|
-
|
|
113
|
+
AMPERSAND,
|
|
118
114
|
)
|
|
119
115
|
: '';
|
|
120
116
|
}
|
|
@@ -123,11 +119,15 @@ export function toQuery(parameters: PlainObject): string {
|
|
|
123
119
|
|
|
124
120
|
// #region Variables
|
|
125
121
|
|
|
122
|
+
const AMPERSAND = '&';
|
|
123
|
+
|
|
124
|
+
const EQUAL = '=';
|
|
125
|
+
|
|
126
126
|
const EXPRESSION_ARRAY_SUFFIX = /\[\]$/;
|
|
127
127
|
|
|
128
128
|
const EXPRESSION_BOOLEAN = /^(false|true)$/;
|
|
129
129
|
|
|
130
|
-
const
|
|
130
|
+
const EXPRESSION_DOT = /\./g;
|
|
131
131
|
|
|
132
132
|
const TRUE = 'true';
|
|
133
133
|
|