1+ /* eslint-disable valid-jsdoc */
12import { isNumber } from 'common/checkVariableTypeUtil' ;
23
34export enum SortingOrder {
@@ -22,4 +23,82 @@ export const sortTwoUnknownValues = (valueA: unknown, valueB: unknown,
2223 }
2324} ;
2425
26+ type Comparator < Item > = ( a : Item , b : Item ) => number ;
2527
28+ const Ordering = {
29+ /** They are equals. */
30+ Equal : 0 ,
31+ /** The first value is sorted after the second. */
32+ First : + 1 ,
33+ /** The second value is sorted after the first. */
34+ Second : - 1 ,
35+ } as const ;
36+
37+ const isNully = ( x : unknown ) : x is null | undefined => {
38+ return x === null || x === undefined ;
39+ } ;
40+
41+ /**
42+ * @param keySelector
43+ * A mapping function to extract key from an array element.
44+ * Note that this function can return null or undefined,
45+ * but these values are treated as greater than other values.
46+ * @param comparatorFn
47+ * This comparator functions are applied in order, starting with the first,
48+ * and if the previous returns 0, the next is applied.
49+ * When all functions s return 0, it means the two elements are equal.
50+ * @return a comparator function.
51+ */
52+ export const buildComparator = < Item , Key > (
53+ keySelector : ( item : Item ) => ( Key | null | undefined ) ,
54+ ...comparatorFn : Comparator < Key > [ ]
55+ ) : Comparator < Item > => {
56+ return ( firstItem , secondItem ) => {
57+ if ( firstItem === secondItem ) return Ordering . Equal ;
58+ const first = keySelector ( firstItem ) ;
59+ const second = keySelector ( secondItem ) ;
60+ if ( first === second ) return Ordering . Equal ;
61+
62+ if ( isNully ( first ) || isNully ( second ) ) {
63+ return ( first === second ) ? Ordering . Equal :
64+ isNully ( first ) ? Ordering . First :
65+ isNully ( second ) ? Ordering . Second :
66+ 'unreachable' as never ;
67+ }
68+
69+ for ( const comparator of comparatorFn ) {
70+ const ordering = comparator ( first , second ) ;
71+ if ( ordering != Ordering . Equal ) return ordering ;
72+ }
73+
74+ return Ordering . Equal ;
75+ } ;
76+ } ;
77+
78+ const numberAscending : Comparator < number > = ( a , b ) => a - b ;
79+ const numberDescending : Comparator < number > = ( a , b ) => b - a ;
80+ const stringAscending : Comparator < string > = ( a , b ) => a . localeCompare ( b ) ;
81+ const stringDescending : Comparator < string > = ( a , b ) => b . localeCompare ( a ) ;
82+ const numericStringAscending = buildComparator ( parseFloat , numberAscending ) ;
83+ const numericStringDescending = buildComparator ( parseFloat , numberDescending ) ;
84+ export const Comparators = {
85+ numberAscending, numberDescending,
86+ stringAscending, stringDescending,
87+ numericStringAscending, numericStringDescending,
88+ } as const ;
89+
90+ export const buildArrayIndexComparator = < Item > (
91+ array : readonly Item [ ] ,
92+ order : SortingOrder = SortingOrder . Ascending ,
93+ ) => {
94+ const comparator = order === SortingOrder . Ascending ?
95+ Comparators . numberAscending :
96+ Comparators . numberDescending ;
97+ return buildComparator < Item , number > (
98+ ( item ) => {
99+ const index = array . indexOf ( item ) ;
100+ return index >= 0 ? index : null ;
101+ } ,
102+ comparator ,
103+ ) ;
104+ } ;
0 commit comments