|
1
|
|
|
import { SortOptions } from './types'; |
|
2
|
1 |
|
import { isSortablePrimitive, hasNullishValues } from './type-guards'; |
|
3
|
1 |
|
import { compareSortablePrimitives } from './comparators'; |
|
4
|
|
|
|
|
5
|
1 |
|
export function sortArray<T>(array: T[], options: SortOptions): T[] { |
|
6
|
72 |
|
if (shouldSortPrimitiveArray(array, options.sortPrimitiveArrays)) { |
|
7
|
34 |
|
return sortPrimitiveArray(array, options.ascending) as T[]; |
|
8
|
|
|
} |
|
9
|
|
|
|
|
10
|
106 |
|
return array.map((item) => sortRecursively(item, options)); |
|
11
|
|
|
} |
|
12
|
|
|
|
|
13
|
|
|
function shouldSortPrimitiveArray( |
|
14
|
|
|
array: unknown[], |
|
15
|
|
|
sortPrimitiveArrays: boolean |
|
16
|
|
|
): boolean { |
|
17
|
72 |
|
return sortPrimitiveArrays && canSortPrimitiveArray(array); |
|
18
|
|
|
} |
|
19
|
|
|
|
|
20
|
|
|
function canSortPrimitiveArray(array: unknown[]): boolean { |
|
21
|
45 |
|
return ( |
|
22
|
|
|
allItemsAreSortablePrimitives(array) && allItemsHaveSameSortableType(array) |
|
23
|
|
|
); |
|
24
|
|
|
} |
|
25
|
|
|
|
|
26
|
|
|
function allItemsAreSortablePrimitives(array: unknown[]): boolean { |
|
27
|
45 |
|
return array.every(isSortablePrimitive); |
|
28
|
|
|
} |
|
29
|
|
|
|
|
30
|
|
|
function sortPrimitiveArray(array: unknown[], ascending: boolean): unknown[] { |
|
31
|
|
|
// The array has already been validated as sortable in canSortPrimitiveArray |
|
32
|
|
|
// No need to check allItemsHaveSameSortableType again |
|
33
|
168 |
|
return [...array].sort((a, b) => compareSortablePrimitives(a, b, ascending)); |
|
34
|
|
|
} |
|
35
|
|
|
|
|
36
|
|
|
function allItemsHaveSameSortableType(array: unknown[]): boolean { |
|
37
|
34 |
|
if (array.length === 0) return true; |
|
38
|
|
|
|
|
39
|
|
|
// Don't sort arrays that contain null or undefined values |
|
40
|
|
|
// as they represent absence of value and don't have a natural ordering |
|
41
|
33 |
|
if (hasNullishValues(array)) { |
|
42
|
|
|
return false; |
|
43
|
|
|
} |
|
44
|
|
|
|
|
45
|
|
|
// For arrays with only sortable primitives, check if they have the same type |
|
46
|
33 |
|
const firstItem = array[0]; |
|
47
|
33 |
|
if (!isSortablePrimitive(firstItem)) return false; |
|
48
|
|
|
|
|
49
|
33 |
|
const expectedType = typeof firstItem; |
|
50
|
120 |
|
const allSameType = array.every((item) => typeof item === expectedType); |
|
51
|
|
|
|
|
52
|
|
|
// If all items are the same type, we can sort them normally |
|
53
|
33 |
|
if (allSameType) return true; |
|
54
|
|
|
|
|
55
|
|
|
// If we have mixed primitive types, we can still "sort" them |
|
56
|
|
|
// (the comparison function will return 0 for different types, maintaining order) |
|
57
|
|
|
// This allows us to cover the fallback case in compareSortablePrimitives |
|
58
|
5 |
|
return array.every(isSortablePrimitive); |
|
59
|
|
|
} |
|
60
|
|
|
|
|
61
|
|
|
import { sortRecursively } from './core'; |
|
62
|
|
|
|