-
-
Notifications
You must be signed in to change notification settings - Fork 44
/
utils.ts
137 lines (126 loc) · 3.38 KB
/
utils.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import type { AttributeToken } from 'pug-lexer';
import type { PugSortAttributes } from './index';
/** Compare result. */
type CompareResult = -1 | 0 | 1;
/** Compare function. */
type CompareFunction<T> = (a: T, b: T) => CompareResult;
/**
* Compare two attributes with each other.
*
* @param a An attribute token.
* @param b An attribute token.
* @param sortAttributes How to sort attributes.
* @param sortAttributesBeginning Attributes that should sorted to the beginning.
* @param sortAttributesEnd Attributes that should sorted to the end.
* @returns The compare result.
*/
export function compareAttributeToken(
a: AttributeToken,
b: AttributeToken,
sortAttributes: PugSortAttributes,
sortAttributesBeginning: string[],
sortAttributesEnd: string[],
): CompareResult {
const sortPatternsBeginning: RegExp[] = sortAttributesBeginning
.map((sort) => new RegExp(sort))
.reverse();
const sortPatternsEnd: RegExp[] = sortAttributesEnd.map(
(sort) => new RegExp(sort),
);
const aName: string = a.name;
const bName: string = b.name;
if (sortPatternsBeginning.length > 0) {
const aBeginningIndex: number = sortPatternsBeginning.findIndex((pattern) =>
pattern.test(aName),
);
const bBeginningIndex: number = sortPatternsBeginning.findIndex((pattern) =>
pattern.test(bName),
);
const beginning: number = aBeginningIndex - bBeginningIndex;
if (beginning > 0) {
return -1;
}
if (beginning < 0) {
return 1;
}
}
if (sortPatternsEnd.length > 0) {
const aEndIndex: number = sortPatternsEnd.findIndex((pattern) =>
pattern.test(aName),
);
const bEndIndex: number = sortPatternsEnd.findIndex((pattern) =>
pattern.test(bName),
);
const end: number = aEndIndex - bEndIndex;
if (end > 0) {
return 1;
}
if (end < 0) {
return -1;
}
}
switch (sortAttributes) {
case 'asc': {
if (aName > bName) {
return 1;
}
if (aName < bName) {
return -1;
}
break;
}
case 'desc': {
if (aName > bName) {
return -1;
}
if (aName < bName) {
return 1;
}
break;
}
}
return 0;
}
/**
* Sort an array with a given compare function.
*
* @param array The array to sort.
* @param compare A function for comparing the values.
* @returns The sorted array.
*/
export function stableSort<T>(
array: ReadonlyArray<T>,
compare: CompareFunction<T>,
): T[] {
const entries: Array<[T, number]> = array.map((value, index) => [
value,
index,
]);
entries.sort((a, b) => {
const order: CompareResult = compare(a[0], b[0]);
// When order is 0, sort by index to make the sort stable
return order === 0 ? a[1] - b[1] : order;
});
return entries.map(([value]) => value);
}
/**
* Partially sorts an array.
*
* @param arr The array to sort.
* @param start The start from where to sort.
* @param end The end to where to sort.
* @param compareFn A function for comparing the values.
* @returns The sorted array.
*/
export function partialSort<T>(
arr: ReadonlyArray<T>,
start: number,
end: number,
compareFn: CompareFunction<T>,
): T[] {
const preSort: T[] = arr.slice(0, start);
const postSort: T[] = arr.slice(end);
const attributes: T[] = arr.slice(start, end);
const sorted: T[] = stableSort(attributes, compareFn);
return [...preSort, ...sorted, ...postSort];
}