@oscarpalmer/atoms 0.173.3 → 0.175.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 +80 -1
- package/dist/array/sort.mjs +72 -17
- package/dist/index.d.mts +80 -1
- package/dist/index.mjs +92 -37
- package/dist/internal/value/compare.mjs +20 -20
- package/package.json +2 -2
- package/src/array/sort.ts +252 -37
- package/src/internal/value/compare.ts +31 -29
package/dist/array/sort.d.mts
CHANGED
|
@@ -65,7 +65,61 @@ 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
|
+
* Get the index for an item _(to be inserted into an array of items)_
|
|
77
|
+
*
|
|
78
|
+
* _(If the array is not sorted, it will be treated as sorted, and the result may be inaccurate)_
|
|
79
|
+
* @param array Array to get the index from
|
|
80
|
+
* @param item Item to get the index for
|
|
81
|
+
* @returns Index for item
|
|
82
|
+
*/
|
|
83
|
+
index(array: Item[], item: Item): number;
|
|
84
|
+
/**
|
|
85
|
+
* Is the array sorted?
|
|
86
|
+
* @param array Array to check
|
|
87
|
+
* @returns `true` if sorted, otherwise `false`
|
|
88
|
+
*/
|
|
89
|
+
is(array: Item[]): boolean;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Get the index for an item _(to be inserted into an array of items)_ based on sorters _(and an optional default direction)_
|
|
93
|
+
*
|
|
94
|
+
* _(If the array is not sorted, it will be treated as sorted, and the result may be inaccurate)_
|
|
95
|
+
* @param array Array to get the index from
|
|
96
|
+
* @param item Item to get the index for
|
|
97
|
+
* @param sorters Sorters to use to determine sorting
|
|
98
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
99
|
+
* @returns Index for item
|
|
100
|
+
*/
|
|
101
|
+
declare function getIndex<Item>(array: Item[], item: Item, sorters: Array<ArraySorter<Item>>, descending?: boolean): number;
|
|
102
|
+
/**
|
|
103
|
+
* Get the index for an item _(to be inserted into an array of items)_ based on a sorter _(and an optional default direction)_
|
|
104
|
+
*
|
|
105
|
+
* _(If the array is not sorted, it will be treated as sorted, and the result may be inaccurate)_
|
|
106
|
+
* @param array Array to get the index from
|
|
107
|
+
* @param item Item to get the index for
|
|
108
|
+
* @param sorter Sorter to use to determine sorting
|
|
109
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
110
|
+
* @returns Index for item
|
|
111
|
+
*/
|
|
112
|
+
declare function getIndex<Item>(array: Item[], item: Item, sorter: ArraySorter<Item>, descending?: boolean): number;
|
|
113
|
+
/**
|
|
114
|
+
* Get the index for an item _(to be inserted into an array of items)_ based on an optional default direction_
|
|
115
|
+
*
|
|
116
|
+
* _(If the array is not sorted, it will be treated as sorted, and the result may be inaccurate)_
|
|
117
|
+
* @param array Array to get the index from
|
|
118
|
+
* @param item Item to get the index for
|
|
119
|
+
* @param descending Sorted in descending order? _(defaults to `false`)_
|
|
120
|
+
* @returns Index for item
|
|
121
|
+
*/
|
|
122
|
+
declare function getIndex<Item>(array: Item[], item: Item, descending?: boolean): number;
|
|
69
123
|
/**
|
|
70
124
|
* Initialize a sort handler with sorters _(and an optional default direction)_
|
|
71
125
|
* @param sorters Sorters to use for sorting
|
|
@@ -86,6 +140,29 @@ declare function initializeSort<Item>(sorter: ArraySorter<Item>, descending?: bo
|
|
|
86
140
|
* @returns Sort handler
|
|
87
141
|
*/
|
|
88
142
|
declare function initializeSort<Item>(descending?: boolean): Sorter<Item>;
|
|
143
|
+
/**
|
|
144
|
+
* Is the array sorted according to the sorters _(and the optional default direction)_?
|
|
145
|
+
* @param array Array to check
|
|
146
|
+
* @param sorters Sorters to determine sorting
|
|
147
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
148
|
+
* @returns `true` if sorted, otherwise `false`
|
|
149
|
+
*/
|
|
150
|
+
declare function isSorted<Item>(array: Item[], sorters: Array<ArraySorter<Item>>, descending?: boolean): boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Is the array sorted according to the sorter _(and the optional default direction)_?
|
|
153
|
+
* @param array Array to check
|
|
154
|
+
* @param sorter Sorter to determine sorting
|
|
155
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
156
|
+
* @returns `true` if sorted, otherwise `false`
|
|
157
|
+
*/
|
|
158
|
+
declare function isSorted<Item>(array: Item[], sorter: ArraySorter<Item>, descending?: boolean): boolean;
|
|
159
|
+
/**
|
|
160
|
+
* Is the array sorted?
|
|
161
|
+
* @param array Array to check
|
|
162
|
+
* @param descending Sorted in descending order? _(defaults to `false`)_
|
|
163
|
+
* @returns `true` if sorted, otherwise `false`
|
|
164
|
+
*/
|
|
165
|
+
declare function isSorted<Item>(array: Item[], descending?: boolean): boolean;
|
|
89
166
|
/**
|
|
90
167
|
* Sort an array of items, using multiple sorters to sort by specific values
|
|
91
168
|
* @param array Array to sort
|
|
@@ -110,7 +187,9 @@ declare function sort<Item>(array: Item[], sorter: ArraySorter<Item>, descending
|
|
|
110
187
|
*/
|
|
111
188
|
declare function sort<Item>(array: Item[], descending?: boolean): Item[];
|
|
112
189
|
declare namespace sort {
|
|
190
|
+
var index: typeof getIndex;
|
|
113
191
|
var initialize: typeof initializeSort;
|
|
192
|
+
var is: typeof isSorted;
|
|
114
193
|
}
|
|
115
194
|
declare const SORT_DIRECTION_ASCENDING: SortDirection;
|
|
116
195
|
declare const SORT_DIRECTION_DESCENDING: SortDirection;
|
package/dist/array/sort.mjs
CHANGED
|
@@ -9,6 +9,18 @@ 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
|
+
}
|
|
21
|
+
function getIndex(array, item, first, second) {
|
|
22
|
+
return getSortedIndex(array, item, getSorters(first, getModifier(first, second)));
|
|
23
|
+
}
|
|
12
24
|
function getModifier(first, second) {
|
|
13
25
|
return modifiers[first === true || second === true ? SORT_DIRECTION_DESCENDING : SORT_DIRECTION_ASCENDING];
|
|
14
26
|
}
|
|
@@ -22,6 +34,22 @@ function getObjectSorter(obj, modifier) {
|
|
|
22
34
|
if (sorter != null && typeof obj.direction === "string") sorter.modifier = modifiers[obj.direction] ?? modifier;
|
|
23
35
|
return sorter;
|
|
24
36
|
}
|
|
37
|
+
function getSortedIndex(array, item, sorters) {
|
|
38
|
+
if (!Array.isArray(array)) return -1;
|
|
39
|
+
const { length } = array;
|
|
40
|
+
if (length === 0) return 0;
|
|
41
|
+
const sortersLength = sorters.length;
|
|
42
|
+
if (getComparisonValue(item, array[0], sorters, sortersLength) < 0) return 0;
|
|
43
|
+
if (getComparisonValue(item, array[length - 1], sorters, sortersLength) >= 0) return length;
|
|
44
|
+
let low = 0;
|
|
45
|
+
let high = length - 1;
|
|
46
|
+
while (low <= high) {
|
|
47
|
+
const mid = Math.floor((low + high) / 2);
|
|
48
|
+
if (getComparisonValue(item, array[mid], sorters, sortersLength) < 0) high = mid - 1;
|
|
49
|
+
else low = mid + 1;
|
|
50
|
+
}
|
|
51
|
+
return low;
|
|
52
|
+
}
|
|
25
53
|
function getSorter(value, modifier) {
|
|
26
54
|
switch (true) {
|
|
27
55
|
case typeof value === "function": return getComparisonSorter(value, modifier);
|
|
@@ -39,6 +67,11 @@ function getSorters(value, modifier) {
|
|
|
39
67
|
const sorter = getSorter(item, modifier);
|
|
40
68
|
if (sorter != null) sorters.push(sorter);
|
|
41
69
|
}
|
|
70
|
+
if (sorters.length === 0) return [{
|
|
71
|
+
modifier,
|
|
72
|
+
get: false,
|
|
73
|
+
identifier: "default"
|
|
74
|
+
}];
|
|
42
75
|
return sorters.filter((value, index, array) => array.findIndex((next) => next.identifier === value.identifier) === index);
|
|
43
76
|
}
|
|
44
77
|
function getValueSorter(value, modifier) {
|
|
@@ -50,30 +83,52 @@ function getValueSorter(value, modifier) {
|
|
|
50
83
|
};
|
|
51
84
|
}
|
|
52
85
|
function initializeSort(first, second) {
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
86
|
+
const sorters = getSorters(first, getModifier(first, second));
|
|
87
|
+
const sorter = (array) => sortArray(array, sorters);
|
|
88
|
+
sorter.index = (array, item) => getSortedIndex(array, item, sorters);
|
|
89
|
+
sorter.is = (array) => isSortedArray(array, sorters);
|
|
90
|
+
return sorter;
|
|
91
|
+
}
|
|
92
|
+
function isSorted(array, first, second) {
|
|
93
|
+
return isSortedArray(array, getSorters(first, getModifier(first, second)));
|
|
94
|
+
}
|
|
95
|
+
function isSortedArray(array, sorters) {
|
|
96
|
+
if (!Array.isArray(array)) return false;
|
|
97
|
+
const { length } = array;
|
|
98
|
+
if (length < 2) return true;
|
|
99
|
+
const sortersLength = sorters.length;
|
|
100
|
+
let offset = 0;
|
|
101
|
+
if (length >= ARRAY_THRESHOLD) {
|
|
102
|
+
offset = Math.round(length / ARRAY_PEEK_PERCENTAGE);
|
|
103
|
+
offset = offset > ARRAY_THRESHOLD ? ARRAY_THRESHOLD : offset;
|
|
104
|
+
for (let index = 0; index < offset; index += 1) {
|
|
105
|
+
const [firstItem, firstOffset] = [array[index], array[index + 1]];
|
|
106
|
+
const [secondItem, secondOffset] = [array[length - index - 2], array[length - index - 1]];
|
|
107
|
+
const [firstComparison, secondComparison] = [getComparisonValue(firstItem, firstOffset, sorters, sortersLength), getComparisonValue(secondItem, secondOffset, sorters, sortersLength)];
|
|
108
|
+
if (firstComparison > 0 || secondComparison > 0) return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const end = length - offset - 1;
|
|
112
|
+
for (let index = offset; index < end; index += 1) {
|
|
113
|
+
const first = array[index];
|
|
114
|
+
const second = array[index + 1];
|
|
115
|
+
if (getComparisonValue(first, second, sorters, sortersLength) > 0) return false;
|
|
116
|
+
}
|
|
117
|
+
return true;
|
|
56
118
|
}
|
|
57
119
|
function sort(array, first, second) {
|
|
58
|
-
|
|
59
|
-
return work(array, getSorters(first, modifier), modifier);
|
|
120
|
+
return sortArray(array, getSorters(first, getModifier(first, second)));
|
|
60
121
|
}
|
|
61
|
-
function
|
|
122
|
+
function sortArray(array, sorters) {
|
|
62
123
|
if (!Array.isArray(array)) return [];
|
|
63
|
-
if (array.length < 2) return array;
|
|
64
124
|
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
|
-
});
|
|
125
|
+
return array.length > 1 ? array.sort((first, second) => getComparisonValue(first, second, sorters, length)) : array;
|
|
75
126
|
}
|
|
127
|
+
sort.index = getIndex;
|
|
76
128
|
sort.initialize = initializeSort;
|
|
129
|
+
sort.is = isSorted;
|
|
130
|
+
const ARRAY_PEEK_PERCENTAGE = 10;
|
|
131
|
+
const ARRAY_THRESHOLD = 100;
|
|
77
132
|
const SORT_DIRECTION_ASCENDING = "ascending";
|
|
78
133
|
const SORT_DIRECTION_DESCENDING = "descending";
|
|
79
134
|
const modifiers = {
|
package/dist/index.d.mts
CHANGED
|
@@ -1166,7 +1166,61 @@ 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
|
+
* Get the index for an item _(to be inserted into an array of items)_
|
|
1178
|
+
*
|
|
1179
|
+
* _(If the array is not sorted, it will be treated as sorted, and the result may be inaccurate)_
|
|
1180
|
+
* @param array Array to get the index from
|
|
1181
|
+
* @param item Item to get the index for
|
|
1182
|
+
* @returns Index for item
|
|
1183
|
+
*/
|
|
1184
|
+
index(array: Item[], item: Item): number;
|
|
1185
|
+
/**
|
|
1186
|
+
* Is the array sorted?
|
|
1187
|
+
* @param array Array to check
|
|
1188
|
+
* @returns `true` if sorted, otherwise `false`
|
|
1189
|
+
*/
|
|
1190
|
+
is(array: Item[]): boolean;
|
|
1191
|
+
};
|
|
1192
|
+
/**
|
|
1193
|
+
* Get the index for an item _(to be inserted into an array of items)_ based on sorters _(and an optional default direction)_
|
|
1194
|
+
*
|
|
1195
|
+
* _(If the array is not sorted, it will be treated as sorted, and the result may be inaccurate)_
|
|
1196
|
+
* @param array Array to get the index from
|
|
1197
|
+
* @param item Item to get the index for
|
|
1198
|
+
* @param sorters Sorters to use to determine sorting
|
|
1199
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
1200
|
+
* @returns Index for item
|
|
1201
|
+
*/
|
|
1202
|
+
declare function getIndex<Item>(array: Item[], item: Item, sorters: Array<ArraySorter<Item>>, descending?: boolean): number;
|
|
1203
|
+
/**
|
|
1204
|
+
* Get the index for an item _(to be inserted into an array of items)_ based on a sorter _(and an optional default direction)_
|
|
1205
|
+
*
|
|
1206
|
+
* _(If the array is not sorted, it will be treated as sorted, and the result may be inaccurate)_
|
|
1207
|
+
* @param array Array to get the index from
|
|
1208
|
+
* @param item Item to get the index for
|
|
1209
|
+
* @param sorter Sorter to use to determine sorting
|
|
1210
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
1211
|
+
* @returns Index for item
|
|
1212
|
+
*/
|
|
1213
|
+
declare function getIndex<Item>(array: Item[], item: Item, sorter: ArraySorter<Item>, descending?: boolean): number;
|
|
1214
|
+
/**
|
|
1215
|
+
* Get the index for an item _(to be inserted into an array of items)_ based on an optional default direction_
|
|
1216
|
+
*
|
|
1217
|
+
* _(If the array is not sorted, it will be treated as sorted, and the result may be inaccurate)_
|
|
1218
|
+
* @param array Array to get the index from
|
|
1219
|
+
* @param item Item to get the index for
|
|
1220
|
+
* @param descending Sorted in descending order? _(defaults to `false`)_
|
|
1221
|
+
* @returns Index for item
|
|
1222
|
+
*/
|
|
1223
|
+
declare function getIndex<Item>(array: Item[], item: Item, descending?: boolean): number;
|
|
1170
1224
|
/**
|
|
1171
1225
|
* Initialize a sort handler with sorters _(and an optional default direction)_
|
|
1172
1226
|
* @param sorters Sorters to use for sorting
|
|
@@ -1187,6 +1241,29 @@ declare function initializeSort<Item>(sorter: ArraySorter<Item>, descending?: bo
|
|
|
1187
1241
|
* @returns Sort handler
|
|
1188
1242
|
*/
|
|
1189
1243
|
declare function initializeSort<Item>(descending?: boolean): Sorter<Item>;
|
|
1244
|
+
/**
|
|
1245
|
+
* Is the array sorted according to the sorters _(and the optional default direction)_?
|
|
1246
|
+
* @param array Array to check
|
|
1247
|
+
* @param sorters Sorters to determine sorting
|
|
1248
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
1249
|
+
* @returns `true` if sorted, otherwise `false`
|
|
1250
|
+
*/
|
|
1251
|
+
declare function isSorted<Item>(array: Item[], sorters: Array<ArraySorter<Item>>, descending?: boolean): boolean;
|
|
1252
|
+
/**
|
|
1253
|
+
* Is the array sorted according to the sorter _(and the optional default direction)_?
|
|
1254
|
+
* @param array Array to check
|
|
1255
|
+
* @param sorter Sorter to determine sorting
|
|
1256
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
1257
|
+
* @returns `true` if sorted, otherwise `false`
|
|
1258
|
+
*/
|
|
1259
|
+
declare function isSorted<Item>(array: Item[], sorter: ArraySorter<Item>, descending?: boolean): boolean;
|
|
1260
|
+
/**
|
|
1261
|
+
* Is the array sorted?
|
|
1262
|
+
* @param array Array to check
|
|
1263
|
+
* @param descending Sorted in descending order? _(defaults to `false`)_
|
|
1264
|
+
* @returns `true` if sorted, otherwise `false`
|
|
1265
|
+
*/
|
|
1266
|
+
declare function isSorted<Item>(array: Item[], descending?: boolean): boolean;
|
|
1190
1267
|
/**
|
|
1191
1268
|
* Sort an array of items, using multiple sorters to sort by specific values
|
|
1192
1269
|
* @param array Array to sort
|
|
@@ -1211,7 +1288,9 @@ declare function sort<Item>(array: Item[], sorter: ArraySorter<Item>, descending
|
|
|
1211
1288
|
*/
|
|
1212
1289
|
declare function sort<Item>(array: Item[], descending?: boolean): Item[];
|
|
1213
1290
|
declare namespace sort {
|
|
1291
|
+
var index: typeof getIndex;
|
|
1214
1292
|
var initialize: typeof initializeSort;
|
|
1293
|
+
var is: typeof isSorted;
|
|
1215
1294
|
}
|
|
1216
1295
|
declare const SORT_DIRECTION_ASCENDING: SortDirection;
|
|
1217
1296
|
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,18 @@ 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
|
+
}
|
|
931
|
+
function getIndex(array, item, first, second) {
|
|
932
|
+
return getSortedIndex(array, item, getSorters(first, getModifier(first, second)));
|
|
933
|
+
}
|
|
922
934
|
function getModifier(first, second) {
|
|
923
935
|
return modifiers[first === true || second === true ? SORT_DIRECTION_DESCENDING : SORT_DIRECTION_ASCENDING];
|
|
924
936
|
}
|
|
@@ -932,6 +944,22 @@ function getObjectSorter(obj, modifier) {
|
|
|
932
944
|
if (sorter != null && typeof obj.direction === "string") sorter.modifier = modifiers[obj.direction] ?? modifier;
|
|
933
945
|
return sorter;
|
|
934
946
|
}
|
|
947
|
+
function getSortedIndex(array, item, sorters) {
|
|
948
|
+
if (!Array.isArray(array)) return -1;
|
|
949
|
+
const { length } = array;
|
|
950
|
+
if (length === 0) return 0;
|
|
951
|
+
const sortersLength = sorters.length;
|
|
952
|
+
if (getComparisonValue(item, array[0], sorters, sortersLength) < 0) return 0;
|
|
953
|
+
if (getComparisonValue(item, array[length - 1], sorters, sortersLength) >= 0) return length;
|
|
954
|
+
let low = 0;
|
|
955
|
+
let high = length - 1;
|
|
956
|
+
while (low <= high) {
|
|
957
|
+
const mid = Math.floor((low + high) / 2);
|
|
958
|
+
if (getComparisonValue(item, array[mid], sorters, sortersLength) < 0) high = mid - 1;
|
|
959
|
+
else low = mid + 1;
|
|
960
|
+
}
|
|
961
|
+
return low;
|
|
962
|
+
}
|
|
935
963
|
function getSorter(value, modifier) {
|
|
936
964
|
switch (true) {
|
|
937
965
|
case typeof value === "function": return getComparisonSorter(value, modifier);
|
|
@@ -949,6 +977,11 @@ function getSorters(value, modifier) {
|
|
|
949
977
|
const sorter = getSorter(item, modifier);
|
|
950
978
|
if (sorter != null) sorters.push(sorter);
|
|
951
979
|
}
|
|
980
|
+
if (sorters.length === 0) return [{
|
|
981
|
+
modifier,
|
|
982
|
+
get: false,
|
|
983
|
+
identifier: "default"
|
|
984
|
+
}];
|
|
952
985
|
return sorters.filter((value, index, array) => array.findIndex((next) => next.identifier === value.identifier) === index);
|
|
953
986
|
}
|
|
954
987
|
function getValueSorter(value, modifier) {
|
|
@@ -960,30 +993,52 @@ function getValueSorter(value, modifier) {
|
|
|
960
993
|
};
|
|
961
994
|
}
|
|
962
995
|
function initializeSort(first, second) {
|
|
963
|
-
const
|
|
964
|
-
const
|
|
965
|
-
|
|
996
|
+
const sorters = getSorters(first, getModifier(first, second));
|
|
997
|
+
const sorter = (array) => sortArray(array, sorters);
|
|
998
|
+
sorter.index = (array, item) => getSortedIndex(array, item, sorters);
|
|
999
|
+
sorter.is = (array) => isSortedArray(array, sorters);
|
|
1000
|
+
return sorter;
|
|
1001
|
+
}
|
|
1002
|
+
function isSorted(array, first, second) {
|
|
1003
|
+
return isSortedArray(array, getSorters(first, getModifier(first, second)));
|
|
1004
|
+
}
|
|
1005
|
+
function isSortedArray(array, sorters) {
|
|
1006
|
+
if (!Array.isArray(array)) return false;
|
|
1007
|
+
const { length } = array;
|
|
1008
|
+
if (length < 2) return true;
|
|
1009
|
+
const sortersLength = sorters.length;
|
|
1010
|
+
let offset = 0;
|
|
1011
|
+
if (length >= ARRAY_THRESHOLD$1) {
|
|
1012
|
+
offset = Math.round(length / ARRAY_PEEK_PERCENTAGE$1);
|
|
1013
|
+
offset = offset > ARRAY_THRESHOLD$1 ? ARRAY_THRESHOLD$1 : offset;
|
|
1014
|
+
for (let index = 0; index < offset; index += 1) {
|
|
1015
|
+
const [firstItem, firstOffset] = [array[index], array[index + 1]];
|
|
1016
|
+
const [secondItem, secondOffset] = [array[length - index - 2], array[length - index - 1]];
|
|
1017
|
+
const [firstComparison, secondComparison] = [getComparisonValue(firstItem, firstOffset, sorters, sortersLength), getComparisonValue(secondItem, secondOffset, sorters, sortersLength)];
|
|
1018
|
+
if (firstComparison > 0 || secondComparison > 0) return false;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
const end = length - offset - 1;
|
|
1022
|
+
for (let index = offset; index < end; index += 1) {
|
|
1023
|
+
const first = array[index];
|
|
1024
|
+
const second = array[index + 1];
|
|
1025
|
+
if (getComparisonValue(first, second, sorters, sortersLength) > 0) return false;
|
|
1026
|
+
}
|
|
1027
|
+
return true;
|
|
966
1028
|
}
|
|
967
1029
|
function sort(array, first, second) {
|
|
968
|
-
|
|
969
|
-
return work$1(array, getSorters(first, modifier), modifier);
|
|
1030
|
+
return sortArray(array, getSorters(first, getModifier(first, second)));
|
|
970
1031
|
}
|
|
971
|
-
function
|
|
1032
|
+
function sortArray(array, sorters) {
|
|
972
1033
|
if (!Array.isArray(array)) return [];
|
|
973
|
-
if (array.length < 2) return array;
|
|
974
1034
|
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
|
-
});
|
|
1035
|
+
return array.length > 1 ? array.sort((first, second) => getComparisonValue(first, second, sorters, length)) : array;
|
|
985
1036
|
}
|
|
1037
|
+
sort.index = getIndex;
|
|
986
1038
|
sort.initialize = initializeSort;
|
|
1039
|
+
sort.is = isSorted;
|
|
1040
|
+
const ARRAY_PEEK_PERCENTAGE$1 = 10;
|
|
1041
|
+
const ARRAY_THRESHOLD$1 = 100;
|
|
987
1042
|
const SORT_DIRECTION_ASCENDING = "ascending";
|
|
988
1043
|
const SORT_DIRECTION_DESCENDING = "descending";
|
|
989
1044
|
const modifiers = {
|
|
@@ -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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oscarpalmer/atoms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.175.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,31 @@ 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
|
+
* Get the index for an item _(to be inserted into an array of items)_
|
|
117
|
+
*
|
|
118
|
+
* _(If the array is not sorted, it will be treated as sorted, and the result may be inaccurate)_
|
|
119
|
+
* @param array Array to get the index from
|
|
120
|
+
* @param item Item to get the index for
|
|
121
|
+
* @returns Index for item
|
|
122
|
+
*/
|
|
123
|
+
index(array: Item[], item: Item): number;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Is the array sorted?
|
|
127
|
+
* @param array Array to check
|
|
128
|
+
* @returns `true` if sorted, otherwise `false`
|
|
129
|
+
*/
|
|
130
|
+
is(array: Item[]): boolean;
|
|
131
|
+
};
|
|
108
132
|
|
|
109
133
|
// #endregion
|
|
110
134
|
|
|
@@ -121,6 +145,82 @@ function getComparisonSorter(callback: Function, modifier: number): InternalSort
|
|
|
121
145
|
};
|
|
122
146
|
}
|
|
123
147
|
|
|
148
|
+
function getComparisonValue(
|
|
149
|
+
first: unknown,
|
|
150
|
+
second: unknown,
|
|
151
|
+
sorters: InternalSorter[],
|
|
152
|
+
length: number,
|
|
153
|
+
): number {
|
|
154
|
+
for (let index = 0; index < length; index += 1) {
|
|
155
|
+
const sorter = sorters[index];
|
|
156
|
+
|
|
157
|
+
const values = [
|
|
158
|
+
sorter.get ? sorter.value!(first as PlainObject) : first,
|
|
159
|
+
sorter.get ? sorter.value!(second as PlainObject) : second,
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
const comparison =
|
|
163
|
+
(sorter.compare?.complex?.(first, values[0], second, values[1]) ??
|
|
164
|
+
sorter.compare?.simple?.(values[0], values[1]) ??
|
|
165
|
+
compare(values[0], values[1])) * sorter.modifier;
|
|
166
|
+
|
|
167
|
+
if (comparison !== 0) {
|
|
168
|
+
return comparison;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Get the index for an item _(to be inserted into an array of items)_ based on sorters _(and an optional default direction)_
|
|
177
|
+
*
|
|
178
|
+
* _(If the array is not sorted, it will be treated as sorted, and the result may be inaccurate)_
|
|
179
|
+
* @param array Array to get the index from
|
|
180
|
+
* @param item Item to get the index for
|
|
181
|
+
* @param sorters Sorters to use to determine sorting
|
|
182
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
183
|
+
* @returns Index for item
|
|
184
|
+
*/
|
|
185
|
+
function getIndex<Item>(
|
|
186
|
+
array: Item[],
|
|
187
|
+
item: Item,
|
|
188
|
+
sorters: Array<ArraySorter<Item>>,
|
|
189
|
+
descending?: boolean,
|
|
190
|
+
): number;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Get the index for an item _(to be inserted into an array of items)_ based on a sorter _(and an optional default direction)_
|
|
194
|
+
*
|
|
195
|
+
* _(If the array is not sorted, it will be treated as sorted, and the result may be inaccurate)_
|
|
196
|
+
* @param array Array to get the index from
|
|
197
|
+
* @param item Item to get the index for
|
|
198
|
+
* @param sorter Sorter to use to determine sorting
|
|
199
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
200
|
+
* @returns Index for item
|
|
201
|
+
*/
|
|
202
|
+
function getIndex<Item>(
|
|
203
|
+
array: Item[],
|
|
204
|
+
item: Item,
|
|
205
|
+
sorter: ArraySorter<Item>,
|
|
206
|
+
descending?: boolean,
|
|
207
|
+
): number;
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get the index for an item _(to be inserted into an array of items)_ based on an optional default direction_
|
|
211
|
+
*
|
|
212
|
+
* _(If the array is not sorted, it will be treated as sorted, and the result may be inaccurate)_
|
|
213
|
+
* @param array Array to get the index from
|
|
214
|
+
* @param item Item to get the index for
|
|
215
|
+
* @param descending Sorted in descending order? _(defaults to `false`)_
|
|
216
|
+
* @returns Index for item
|
|
217
|
+
*/
|
|
218
|
+
function getIndex<Item>(array: Item[], item: Item, descending?: boolean): number;
|
|
219
|
+
|
|
220
|
+
function getIndex(array: unknown[], item: unknown, first?: unknown, second?: unknown): number {
|
|
221
|
+
return getSortedIndex(array, item, getSorters(first, getModifier(first, second)));
|
|
222
|
+
}
|
|
223
|
+
|
|
124
224
|
function getModifier(first: unknown, second: unknown): number {
|
|
125
225
|
const direction =
|
|
126
226
|
first === true || second === true ? SORT_DIRECTION_DESCENDING : SORT_DIRECTION_ASCENDING;
|
|
@@ -152,6 +252,43 @@ function getObjectSorter(obj: PlainObject, modifier: number): InternalSorter | u
|
|
|
152
252
|
return sorter;
|
|
153
253
|
}
|
|
154
254
|
|
|
255
|
+
function getSortedIndex(array: unknown[], item: unknown, sorters: InternalSorter[]): number {
|
|
256
|
+
if (!Array.isArray(array)) {
|
|
257
|
+
return -1;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const {length} = array;
|
|
261
|
+
|
|
262
|
+
if (length === 0) {
|
|
263
|
+
return 0;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const sortersLength = sorters.length;
|
|
267
|
+
|
|
268
|
+
if (getComparisonValue(item, array[0], sorters, sortersLength) < 0) {
|
|
269
|
+
return 0;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (getComparisonValue(item, array[length - 1], sorters, sortersLength) >= 0) {
|
|
273
|
+
return length;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
let low = 0;
|
|
277
|
+
let high = length - 1;
|
|
278
|
+
|
|
279
|
+
while (low <= high) {
|
|
280
|
+
const mid = Math.floor((low + high) / 2);
|
|
281
|
+
|
|
282
|
+
if (getComparisonValue(item, array[mid], sorters, sortersLength) < 0) {
|
|
283
|
+
high = mid - 1;
|
|
284
|
+
} else {
|
|
285
|
+
low = mid + 1;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return low;
|
|
290
|
+
}
|
|
291
|
+
|
|
155
292
|
function getSorter(value: unknown, modifier: number): InternalSorter | undefined {
|
|
156
293
|
switch (true) {
|
|
157
294
|
case typeof value === 'function':
|
|
@@ -184,6 +321,16 @@ function getSorters(value: unknown, modifier: number): InternalSorter[] {
|
|
|
184
321
|
}
|
|
185
322
|
}
|
|
186
323
|
|
|
324
|
+
if (sorters.length === 0) {
|
|
325
|
+
return [
|
|
326
|
+
{
|
|
327
|
+
modifier,
|
|
328
|
+
get: false,
|
|
329
|
+
identifier: 'default',
|
|
330
|
+
},
|
|
331
|
+
];
|
|
332
|
+
}
|
|
333
|
+
|
|
187
334
|
return sorters.filter(
|
|
188
335
|
(value, index, array) =>
|
|
189
336
|
array.findIndex(next => next.identifier === value.identifier) === index,
|
|
@@ -226,10 +373,98 @@ function initializeSort<Item>(sorter: ArraySorter<Item>, descending?: boolean):
|
|
|
226
373
|
function initializeSort<Item>(descending?: boolean): Sorter<Item>;
|
|
227
374
|
|
|
228
375
|
function initializeSort(first?: unknown, second?: unknown): Sorter<unknown> {
|
|
229
|
-
const
|
|
230
|
-
|
|
376
|
+
const sorters = getSorters(first, getModifier(first, second));
|
|
377
|
+
|
|
378
|
+
const sorter = (array: unknown[]) => sortArray(array, sorters);
|
|
231
379
|
|
|
232
|
-
|
|
380
|
+
sorter.index = (array: unknown[], item: unknown) => getSortedIndex(array, item, sorters);
|
|
381
|
+
sorter.is = (array: unknown[]) => isSortedArray(array, sorters);
|
|
382
|
+
|
|
383
|
+
return sorter as unknown as Sorter<unknown>;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Is the array sorted according to the sorters _(and the optional default direction)_?
|
|
388
|
+
* @param array Array to check
|
|
389
|
+
* @param sorters Sorters to determine sorting
|
|
390
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
391
|
+
* @returns `true` if sorted, otherwise `false`
|
|
392
|
+
*/
|
|
393
|
+
function isSorted<Item>(
|
|
394
|
+
array: Item[],
|
|
395
|
+
sorters: Array<ArraySorter<Item>>,
|
|
396
|
+
descending?: boolean,
|
|
397
|
+
): boolean;
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Is the array sorted according to the sorter _(and the optional default direction)_?
|
|
401
|
+
* @param array Array to check
|
|
402
|
+
* @param sorter Sorter to determine sorting
|
|
403
|
+
* @param descending Sorted in descending order? _(defaults to `false`; overridden by individual sorters)_
|
|
404
|
+
* @returns `true` if sorted, otherwise `false`
|
|
405
|
+
*/
|
|
406
|
+
function isSorted<Item>(array: Item[], sorter: ArraySorter<Item>, descending?: boolean): boolean;
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Is the array sorted?
|
|
410
|
+
* @param array Array to check
|
|
411
|
+
* @param descending Sorted in descending order? _(defaults to `false`)_
|
|
412
|
+
* @returns `true` if sorted, otherwise `false`
|
|
413
|
+
*/
|
|
414
|
+
function isSorted<Item>(array: Item[], descending?: boolean): boolean;
|
|
415
|
+
|
|
416
|
+
function isSorted(array: unknown[], first?: unknown, second?: unknown): boolean {
|
|
417
|
+
return isSortedArray(array, getSorters(first, getModifier(first, second)));
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function isSortedArray(array: unknown[], sorters: InternalSorter[]): boolean {
|
|
421
|
+
if (!Array.isArray(array)) {
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
const {length} = array;
|
|
426
|
+
|
|
427
|
+
if (length < 2) {
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const sortersLength = sorters.length;
|
|
432
|
+
|
|
433
|
+
let offset = 0;
|
|
434
|
+
|
|
435
|
+
if (length >= ARRAY_THRESHOLD) {
|
|
436
|
+
offset = Math.round(length / ARRAY_PEEK_PERCENTAGE);
|
|
437
|
+
offset = offset > ARRAY_THRESHOLD ? ARRAY_THRESHOLD : offset;
|
|
438
|
+
|
|
439
|
+
for (let index = 0; index < offset; index += 1) {
|
|
440
|
+
const [firstItem, firstOffset] = [array[index], array[index + 1]];
|
|
441
|
+
const [secondItem, secondOffset] = [array[length - index - 2], array[length - index - 1]];
|
|
442
|
+
|
|
443
|
+
const [firstComparison, secondComparison] = [
|
|
444
|
+
getComparisonValue(firstItem, firstOffset, sorters, sortersLength),
|
|
445
|
+
getComparisonValue(secondItem, secondOffset, sorters, sortersLength),
|
|
446
|
+
];
|
|
447
|
+
|
|
448
|
+
if (firstComparison > 0 || secondComparison > 0) {
|
|
449
|
+
return false;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const end = length - offset - 1;
|
|
455
|
+
|
|
456
|
+
for (let index = offset; index < end; index += 1) {
|
|
457
|
+
const first = array[index];
|
|
458
|
+
const second = array[index + 1];
|
|
459
|
+
|
|
460
|
+
const comparison = getComparisonValue(first, second, sorters, sortersLength);
|
|
461
|
+
|
|
462
|
+
if (comparison > 0) {
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return true;
|
|
233
468
|
}
|
|
234
469
|
|
|
235
470
|
/**
|
|
@@ -263,55 +498,35 @@ export function sort<Item>(array: Item[], sorter: ArraySorter<Item>, descending?
|
|
|
263
498
|
export function sort<Item>(array: Item[], descending?: boolean): Item[];
|
|
264
499
|
|
|
265
500
|
export function sort(array: unknown[], first?: unknown, second?: unknown): unknown[] {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
return work(array, getSorters(first, modifier), modifier);
|
|
501
|
+
return sortArray(array, getSorters(first, getModifier(first, second)));
|
|
269
502
|
}
|
|
270
503
|
|
|
271
|
-
function
|
|
504
|
+
function sortArray(array: unknown[], sorters: InternalSorter[]): unknown[] {
|
|
272
505
|
if (!Array.isArray(array)) {
|
|
273
506
|
return [];
|
|
274
507
|
}
|
|
275
508
|
|
|
276
|
-
if (array.length < 2) {
|
|
277
|
-
return array;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
509
|
const {length} = sorters;
|
|
281
510
|
|
|
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
|
-
});
|
|
511
|
+
return array.length > 1
|
|
512
|
+
? array.sort((first, second) => getComparisonValue(first, second, sorters, length))
|
|
513
|
+
: array;
|
|
307
514
|
}
|
|
308
515
|
|
|
516
|
+
sort.index = getIndex;
|
|
517
|
+
|
|
309
518
|
sort.initialize = initializeSort;
|
|
310
519
|
|
|
520
|
+
sort.is = isSorted;
|
|
521
|
+
|
|
311
522
|
// #endregion
|
|
312
523
|
|
|
313
524
|
// #region Variables
|
|
314
525
|
|
|
526
|
+
const ARRAY_PEEK_PERCENTAGE = 10;
|
|
527
|
+
|
|
528
|
+
const ARRAY_THRESHOLD = 100;
|
|
529
|
+
|
|
315
530
|
export const SORT_DIRECTION_ASCENDING: SortDirection = 'ascending';
|
|
316
531
|
|
|
317
532
|
export const SORT_DIRECTION_DESCENDING: SortDirection = 'descending';
|
|
@@ -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
|
};
|