@zoodogood/utils 6.0.0-change.4442 → 7.0.0-change.4513
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/lib/objectives/GlitchText.js +2 -2
- package/lib/objectives/mod.d.ts +2 -2
- package/lib/objectives/mod.d.ts.map +1 -1
- package/lib/objectives/mod.js +2 -2
- package/lib/objectives/randomElementFromArray.d.ts +37 -0
- package/lib/objectives/randomElementFromArray.d.ts.map +1 -0
- package/lib/objectives/randomElementFromArray.js +219 -0
- package/lib/objectives/randomNumberInRange.d.ts +8 -0
- package/lib/objectives/randomNumberInRange.d.ts.map +1 -0
- package/lib/objectives/randomNumberInRange.js +8 -0
- package/lib/primitives/mod.d.ts +1 -4
- package/lib/primitives/mod.d.ts.map +1 -1
- package/lib/primitives/mod.js +1 -11
- package/lib/primitives/pluralization/index.d.ts +13 -0
- package/lib/primitives/pluralization/index.d.ts.map +1 -0
- package/lib/primitives/pluralization/index.js +107 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomNumberInRange } from "./randomNumberInRange.js";
|
|
2
2
|
class GlitchText {
|
|
3
3
|
constructor(from = "", to = "hello, world", { step = 15, random = false, maximum = 100 } = {}) {
|
|
4
4
|
this.from = from;
|
|
@@ -18,7 +18,7 @@ class GlitchText {
|
|
|
18
18
|
word.pop();
|
|
19
19
|
if (word.length < target.length)
|
|
20
20
|
word.push(String.fromCharCode(~~(Math.random() * 50)));
|
|
21
|
-
word.forEach((_, index, array) => array[index] = String.fromCharCode(
|
|
21
|
+
word.forEach((_, index, array) => array[index] = String.fromCharCode(randomNumberInRange({ min: MIN, max: MAX })));
|
|
22
22
|
yield word.join("");
|
|
23
23
|
}
|
|
24
24
|
if (this.maximum)
|
package/lib/objectives/mod.d.ts
CHANGED
|
@@ -2,10 +2,10 @@ export * from "./BusyNumeric.js";
|
|
|
2
2
|
export * from "./CustomCollector.js";
|
|
3
3
|
export * from "./DotNotatedInterface.js";
|
|
4
4
|
export * from "./ExtendedEnum/mod.js";
|
|
5
|
-
export * from "./getRandomElementFromArray.js";
|
|
6
|
-
export * from "./getRandomNumberInRange.js";
|
|
7
5
|
export * from "./GlitchText.js";
|
|
8
6
|
export * from "./LazySort/LazySort.build.js";
|
|
7
|
+
export * from "./randomElementFromArray.js";
|
|
8
|
+
export * from "./randomNumberInRange.js";
|
|
9
9
|
export * from "./rangeToArray.js";
|
|
10
10
|
declare function omit(object: object, filter: CallableFunction): {
|
|
11
11
|
[k: string]: any;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../src/objectives/mod.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../src/objectives/mod.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC;AAChC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC;AACzC,cAAc,mBAAmB,CAAC;AAElC,iBAAS,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB;;EAMrD;AAED,OAAO,EAAE,IAAI,EAAE,CAAC"}
|
package/lib/objectives/mod.js
CHANGED
|
@@ -2,10 +2,10 @@ export * from "./BusyNumeric.js";
|
|
|
2
2
|
export * from "./CustomCollector.js";
|
|
3
3
|
export * from "./DotNotatedInterface.js";
|
|
4
4
|
export * from "./ExtendedEnum/mod.js";
|
|
5
|
-
export * from "./getRandomElementFromArray.js";
|
|
6
|
-
export * from "./getRandomNumberInRange.js";
|
|
7
5
|
export * from "./GlitchText.js";
|
|
8
6
|
export * from "./LazySort/LazySort.build.js";
|
|
7
|
+
export * from "./randomElementFromArray.js";
|
|
8
|
+
export * from "./randomNumberInRange.js";
|
|
9
9
|
export * from "./rangeToArray.js";
|
|
10
10
|
function omit(object, filter) {
|
|
11
11
|
const entries = Object.entries(object).filter(([key, value], i) => filter(key, value, i));
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { BusyNumeric } from "./BusyNumeric.js";
|
|
2
|
+
export declare const _WEIGHT_AUTO = 1000001;
|
|
3
|
+
interface IParams<T> {
|
|
4
|
+
associatedWeights?: number[] | typeof _WEIGHT_AUTO;
|
|
5
|
+
filter?: (item: T) => boolean;
|
|
6
|
+
}
|
|
7
|
+
interface IPickContext<T> {
|
|
8
|
+
item: T;
|
|
9
|
+
pick: (context: IPickContext<T>) => boolean;
|
|
10
|
+
threshold: number;
|
|
11
|
+
point: number;
|
|
12
|
+
index_of_item: number;
|
|
13
|
+
}
|
|
14
|
+
interface ILotteryContext {
|
|
15
|
+
index_of_item: number;
|
|
16
|
+
point: number;
|
|
17
|
+
remove_current_from_pull(): void;
|
|
18
|
+
}
|
|
19
|
+
export declare class RandomizerContext<T> {
|
|
20
|
+
hotel: BusyNumeric;
|
|
21
|
+
array: T[];
|
|
22
|
+
thresholds?: number[];
|
|
23
|
+
constructor(array: T[], thresholds?: number[]);
|
|
24
|
+
static from<T>({ array, associatedWeights, }: {
|
|
25
|
+
array: T[];
|
|
26
|
+
associatedWeights?: number[];
|
|
27
|
+
}): RandomizerContext<T>;
|
|
28
|
+
pickRandom(pick?: IPickContext<T>["pick"]): T | undefined;
|
|
29
|
+
playLottery(): ILotteryContext | undefined;
|
|
30
|
+
}
|
|
31
|
+
export declare function pickInThresholds(thresholds: number[], value: number): number;
|
|
32
|
+
export declare function thresholdsOf(weights: NonNullable<number[]>): number[];
|
|
33
|
+
export declare function randomElementIndexInWeights(weights: NonNullable<number[]>): number | never;
|
|
34
|
+
export declare function randomElementFromArray<T>(array: T[], { associatedWeights, filter }?: IParams<T>): T | undefined;
|
|
35
|
+
export declare function randomElementsFromArray<T>(array: T[], amount?: number, { associatedWeights, filter }?: IParams<T>): T[];
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=randomElementFromArray.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"randomElementFromArray.d.ts","sourceRoot":"","sources":["../../src/objectives/randomElementFromArray.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAgB,MAAM,kBAAkB,CAAC;AAG7D,eAAO,MAAM,YAAY,UAAY,CAAC;AACtC,UAAU,OAAO,CAAC,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,YAAY,CAAC;IACnD,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC;CAC9B;AAED,UAAU,YAAY,CAAC,CAAC;IACvB,IAAI,EAAE,CAAC,CAAC;IACR,IAAI,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,eAAe;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,wBAAwB,IAAI,IAAI,CAAC;CACjC;AAED,qBAAa,iBAAiB,CAAC,CAAC;IAC/B,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;gBACV,KAAK,EAAE,CAAC,EAAE,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE;IAc7C,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,EACd,KAAK,EACL,iBAAiB,GACjB,EAAE;QACF,KAAK,EAAE,CAAC,EAAE,CAAC;QACX,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC7B;IAID,UAAU,CAAC,IAAI,GAAE,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,CAAc,GAAG,CAAC,GAAG,SAAS;IAsDrE,WAAW,IAAI,eAAe,GAAG,SAAS;CAiD1C;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAY5E;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,GAGG,MAAM,EAAE,CACrE;AAED,wBAAgB,2BAA2B,CAC1C,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,GAC5B,MAAM,GAAG,KAAK,CAKhB;AAkBD,wBAAgB,sBAAsB,CAAC,CAAC,EACvC,KAAK,EAAE,CAAC,EAAE,EACV,EAAE,iBAAiB,EAAE,MAAmB,EAAE,GAAE,OAAO,CAAC,CAAC,CAAM,iBA8B3D;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EACxC,KAAK,EAAE,CAAC,EAAE,EACV,MAAM,SAAI,EACV,EAAE,iBAAiB,EAAE,MAAmB,EAAE,GAAE,OAAO,CAAC,CAAC,CAAM,OAiD3D"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import assert from "node:assert";
|
|
2
|
+
import { binary_search } from "../primitives/binary_search.js";
|
|
3
|
+
import { BusyNumeric, size_of_area } from "./BusyNumeric.js";
|
|
4
|
+
import { randomNumberInRange } from "./randomNumberInRange.js";
|
|
5
|
+
export const _WEIGHT_AUTO = 1_000_001;
|
|
6
|
+
export class RandomizerContext {
|
|
7
|
+
hotel;
|
|
8
|
+
array;
|
|
9
|
+
thresholds;
|
|
10
|
+
constructor(array, thresholds) {
|
|
11
|
+
this.array = array;
|
|
12
|
+
this.thresholds = thresholds;
|
|
13
|
+
const range = thresholds?.at(-1) ?? array.length - 1;
|
|
14
|
+
this.hotel = new BusyNumeric(range);
|
|
15
|
+
}
|
|
16
|
+
/* Add to your code
|
|
17
|
+
if (associatedWeights === AUTO) {
|
|
18
|
+
// @ts-expect-error User known about AUTO keyword
|
|
19
|
+
associatedWeights = array.map(($) => $._weight);
|
|
20
|
+
}
|
|
21
|
+
associatedWeights?.length && assert(associatedWeights.at(-1)! < Number.MAX_SAFE_INTEGER)
|
|
22
|
+
*/
|
|
23
|
+
static from({ array, associatedWeights, }) {
|
|
24
|
+
const thresholds = associatedWeights && thresholdsOf(associatedWeights);
|
|
25
|
+
return new RandomizerContext(array, thresholds);
|
|
26
|
+
}
|
|
27
|
+
pickRandom(pick = () => true) {
|
|
28
|
+
const { hotel, thresholds, array } = this;
|
|
29
|
+
while (true) {
|
|
30
|
+
const segments_count = hotel.segments_count();
|
|
31
|
+
if (!segments_count) {
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
const target_of_free = randomNumberInRange({
|
|
35
|
+
max: hotel.free_area - 1,
|
|
36
|
+
});
|
|
37
|
+
const position = (() => {
|
|
38
|
+
let segment = 0;
|
|
39
|
+
let distance = target_of_free;
|
|
40
|
+
let position = 0;
|
|
41
|
+
while (true) {
|
|
42
|
+
const free_area = (hotel.busy_areas[segment]?.[0] ?? hotel.range + 1) -
|
|
43
|
+
(hotel.busy_areas[segment - 1]?.[1] ?? -1) -
|
|
44
|
+
1;
|
|
45
|
+
distance -= free_area;
|
|
46
|
+
position += free_area;
|
|
47
|
+
if (distance < 0) {
|
|
48
|
+
return position + distance;
|
|
49
|
+
}
|
|
50
|
+
position += size_of_area(hotel.busy_areas[segment] || [0, hotel.range + 1]);
|
|
51
|
+
segment++;
|
|
52
|
+
}
|
|
53
|
+
})();
|
|
54
|
+
const index = thresholds
|
|
55
|
+
? pickInThresholds(thresholds, position)
|
|
56
|
+
: position;
|
|
57
|
+
const element = array[index];
|
|
58
|
+
const pickContext = {
|
|
59
|
+
item: element,
|
|
60
|
+
index_of_item: index,
|
|
61
|
+
pick,
|
|
62
|
+
threshold: index,
|
|
63
|
+
point: position,
|
|
64
|
+
};
|
|
65
|
+
const picked = pick(pickContext);
|
|
66
|
+
if (picked) {
|
|
67
|
+
return element;
|
|
68
|
+
}
|
|
69
|
+
thresholds
|
|
70
|
+
? hotel.insert_area(thresholds[index - 1] + 1 || 0, thresholds[index])
|
|
71
|
+
: hotel.bifurcate(index);
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
playLottery() {
|
|
76
|
+
const { hotel, thresholds } = this;
|
|
77
|
+
while (true) {
|
|
78
|
+
const segments_count = hotel.segments_count();
|
|
79
|
+
if (!segments_count) {
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
const target_of_free = randomNumberInRange({
|
|
83
|
+
max: hotel.free_area - 1,
|
|
84
|
+
});
|
|
85
|
+
const position = (() => {
|
|
86
|
+
let segment = 0;
|
|
87
|
+
let distance = target_of_free;
|
|
88
|
+
let position = 0;
|
|
89
|
+
while (true) {
|
|
90
|
+
const free_area = (hotel.busy_areas[segment]?.[0] ?? hotel.range + 1) -
|
|
91
|
+
(hotel.busy_areas[segment - 1]?.[1] ?? -1) -
|
|
92
|
+
1;
|
|
93
|
+
distance -= free_area;
|
|
94
|
+
position += free_area;
|
|
95
|
+
if (distance < 0) {
|
|
96
|
+
return position + distance;
|
|
97
|
+
}
|
|
98
|
+
position += size_of_area(hotel.busy_areas[segment] || [0, hotel.range + 1]);
|
|
99
|
+
segment++;
|
|
100
|
+
}
|
|
101
|
+
})();
|
|
102
|
+
const index = thresholds
|
|
103
|
+
? pickInThresholds(thresholds, position)
|
|
104
|
+
: position;
|
|
105
|
+
const lotteryContext = {
|
|
106
|
+
index_of_item: index,
|
|
107
|
+
point: position,
|
|
108
|
+
remove_current_from_pull() {
|
|
109
|
+
thresholds
|
|
110
|
+
? hotel.insert_area(thresholds[index - 1] + 1 || 0, thresholds[index])
|
|
111
|
+
: hotel.bifurcate(index);
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
return lotteryContext;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export function pickInThresholds(thresholds, value) {
|
|
119
|
+
assert(thresholds.length > 0);
|
|
120
|
+
return binary_search(thresholds.length - 1, (index) => {
|
|
121
|
+
const hold = thresholds[index];
|
|
122
|
+
if (hold >= value && (thresholds[index - 1] ?? -1) < value) {
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
if (hold < value) {
|
|
126
|
+
return -1;
|
|
127
|
+
}
|
|
128
|
+
return 1;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
export function thresholdsOf(weights) {
|
|
132
|
+
assert(weights.length > 0, "Invalid array length");
|
|
133
|
+
let previousLimit = 0;
|
|
134
|
+
return weights.map((weight) => (previousLimit += weight));
|
|
135
|
+
}
|
|
136
|
+
export function randomElementIndexInWeights(weights) {
|
|
137
|
+
const thresholds = thresholdsOf(weights);
|
|
138
|
+
const lotteryPick = Math.random() * thresholds.at(-1);
|
|
139
|
+
return pickInThresholds(thresholds, lotteryPick);
|
|
140
|
+
}
|
|
141
|
+
// task: честный алгоритм , для которого каждый из элеметов списка имеет равный шанс на реализацию своей вероятности.
|
|
142
|
+
// Обычно весь список фильтруется и после выбирается элемент. В нашем случае фильтр будет срабатывать только для тех элементов,
|
|
143
|
+
// которые на этапе фильтрации «выбраны» — то есть имеют свою стопроцентную вероятность быть окончательными после успешного прохождения фильтра
|
|
144
|
+
// требования: честная вероятность; итоговая производительность должна быть выше обычной при проходе списка фильтром
|
|
145
|
+
// Алгоритм:
|
|
146
|
+
/*
|
|
147
|
+
Оптимальный алгоритм поиска случайного элемента списка, удовлетворяющего условию. Поддерживается возможность задавать элементам коэффициент вероятности
|
|
148
|
+
|
|
149
|
+
Перед выбором элемента выбираем случайный сегмент, далее случайный элемент в этом сегменте
|
|
150
|
+
|
|
151
|
+
Если соответствует условию, возвращаем элемент. Завершаем поиск
|
|
152
|
+
|
|
153
|
+
Иначе исключаем элемент путем раздвоения текущего сегмента так, чтобы элемент не оказался ни в одном сегменте списка
|
|
154
|
+
|
|
155
|
+
Повторяем
|
|
156
|
+
*/
|
|
157
|
+
export function randomElementFromArray(array, { associatedWeights, filter = () => true } = {}) {
|
|
158
|
+
assert(array.length, "Invalid array length");
|
|
159
|
+
if (associatedWeights === _WEIGHT_AUTO) {
|
|
160
|
+
// @ts-expect-error User know about AUTO keysymbol
|
|
161
|
+
associatedWeights = array.map(($) => $._weight);
|
|
162
|
+
}
|
|
163
|
+
associatedWeights?.length &&
|
|
164
|
+
assert(associatedWeights.at(-1) < Number.MAX_SAFE_INTEGER);
|
|
165
|
+
assert(!associatedWeights || associatedWeights.length === array.length, "Incorrectly passed argument associatedWeights: The length of the associatedWeights must exactly match the length of the weights array");
|
|
166
|
+
if (array.length === 1) {
|
|
167
|
+
return array[0];
|
|
168
|
+
}
|
|
169
|
+
// light strategy
|
|
170
|
+
if (!filter) {
|
|
171
|
+
if (associatedWeights) {
|
|
172
|
+
return array[randomElementIndexInWeights(associatedWeights)];
|
|
173
|
+
}
|
|
174
|
+
return array[Math.floor(Math.random() * array.length)];
|
|
175
|
+
}
|
|
176
|
+
return RandomizerContext.from({
|
|
177
|
+
array,
|
|
178
|
+
associatedWeights,
|
|
179
|
+
}).pickRandom(({ item }) => filter(item));
|
|
180
|
+
}
|
|
181
|
+
export function randomElementsFromArray(array, amount = 1, { associatedWeights, filter = () => true } = {}) {
|
|
182
|
+
assert(array.length);
|
|
183
|
+
assert(amount <= array.length);
|
|
184
|
+
assert(amount > 1, `Current amount: ${amount}, use randomElementFromArray instead of randomElement**s**FromArray if amount is 1`);
|
|
185
|
+
if (associatedWeights === _WEIGHT_AUTO) {
|
|
186
|
+
// @ts-expect-error User know about AUTO keysymbol
|
|
187
|
+
associatedWeights = array.map(($) => $._weight);
|
|
188
|
+
}
|
|
189
|
+
associatedWeights?.length &&
|
|
190
|
+
assert(associatedWeights.at(-1) < Number.MAX_SAFE_INTEGER);
|
|
191
|
+
assert(!associatedWeights || associatedWeights.length === array.length, "Incorrectly passed argument associatedWeights: The length of the associatedWeights must exactly match the length of the weights array");
|
|
192
|
+
if (amount === array.length) {
|
|
193
|
+
return Array.from(array);
|
|
194
|
+
}
|
|
195
|
+
// light strategy
|
|
196
|
+
if (!filter && !associatedWeights) {
|
|
197
|
+
const _array = Array.from(array);
|
|
198
|
+
return Array.from({ length: amount }, () => _array.splice(Math.floor(Math.random() * _array.length), 1)[0]);
|
|
199
|
+
}
|
|
200
|
+
const random_ctx = RandomizerContext.from({
|
|
201
|
+
array,
|
|
202
|
+
associatedWeights,
|
|
203
|
+
});
|
|
204
|
+
return Array.from({ length: amount }, (_, i) => {
|
|
205
|
+
while (true) {
|
|
206
|
+
const lottery_ctx = random_ctx.playLottery();
|
|
207
|
+
if (!lottery_ctx) {
|
|
208
|
+
// BEFORE PULL TO-DO вероятно неграммотное сообщение об ошибке
|
|
209
|
+
throw new Error("Not enought items was pass filter");
|
|
210
|
+
}
|
|
211
|
+
lottery_ctx.remove_current_from_pull();
|
|
212
|
+
const item = array[lottery_ctx.index_of_item];
|
|
213
|
+
if (!filter(item)) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
return item;
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface IGetRandomValueOptions {
|
|
2
|
+
min?: number;
|
|
3
|
+
max: number;
|
|
4
|
+
needRound?: boolean;
|
|
5
|
+
}
|
|
6
|
+
declare function randomNumberInRange({ min, max, needRound, }: IGetRandomValueOptions): number;
|
|
7
|
+
export { randomNumberInRange };
|
|
8
|
+
//# sourceMappingURL=randomNumberInRange.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"randomNumberInRange.d.ts","sourceRoot":"","sources":["../../src/objectives/randomNumberInRange.ts"],"names":[],"mappings":"AAAA,UAAU,sBAAsB;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,iBAAS,mBAAmB,CAAC,EAC5B,GAAO,EACP,GAAG,EACH,SAAgB,GAChB,EAAE,sBAAsB,UAOxB;AAED,OAAO,EAAE,mBAAmB,EAAE,CAAC"}
|
package/lib/primitives/mod.d.ts
CHANGED
|
@@ -3,12 +3,9 @@ export * from "./BracketsParser.js";
|
|
|
3
3
|
export { CliParser } from "./CliParser.js";
|
|
4
4
|
export * from "./createDefaultPreventable.js";
|
|
5
5
|
export * from "./normalize.js";
|
|
6
|
+
export * from './pluralization/index.js';
|
|
6
7
|
export * from "./symbols.js";
|
|
7
8
|
export * from "./TextTableBuilder.js";
|
|
8
|
-
interface IEndingOptions {
|
|
9
|
-
unite?: (quantity: number, word: string) => string;
|
|
10
|
-
}
|
|
11
|
-
export declare function ending(quantity: number, base: string, multiple: string, alone: string, double: string, options?: IEndingOptions): string | number;
|
|
12
9
|
export declare function sortMutByResolve<T>(array: T[], resolve: ($: T) => number, { reverse }?: {
|
|
13
10
|
reverse?: boolean;
|
|
14
11
|
}): T[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../src/primitives/mod.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,
|
|
1
|
+
{"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../../src/primitives/mod.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AAGtC,wBAAgB,gBAAgB,CAAC,CAAC,EACjC,KAAK,EAAE,CAAC,EAAE,EACV,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,EACzB,EAAE,OAAO,EAAE,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,OAKvC;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,OAG5C;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,aAAa,EAAE,CAAC,WAO9D"}
|
package/lib/primitives/mod.js
CHANGED
|
@@ -3,19 +3,9 @@ export * from "./BracketsParser.js";
|
|
|
3
3
|
export { CliParser } from "./CliParser.js";
|
|
4
4
|
export * from "./createDefaultPreventable.js";
|
|
5
5
|
export * from "./normalize.js";
|
|
6
|
+
export * from './pluralization/index.js';
|
|
6
7
|
export * from "./symbols.js";
|
|
7
8
|
export * from "./TextTableBuilder.js";
|
|
8
|
-
export function ending(quantity, base, multiple, alone, double, options = {}) {
|
|
9
|
-
if (Number.isNaN(+quantity)) {
|
|
10
|
-
return Number.NaN;
|
|
11
|
-
}
|
|
12
|
-
const target = quantity % 100 < 20 ? quantity % 20 : quantity % 10;
|
|
13
|
-
const end = target >= 5 || target === 0 ? multiple : target > 1 ? double : alone;
|
|
14
|
-
const word = base + end;
|
|
15
|
-
options.unite ||= (quantity, word) => `${quantity} ${word}`;
|
|
16
|
-
const input = options.unite(quantity, word);
|
|
17
|
-
return input;
|
|
18
|
-
}
|
|
19
9
|
export function sortMutByResolve(array, resolve, { reverse } = {}) {
|
|
20
10
|
return reverse
|
|
21
11
|
? array.sort((a, b) => resolve(a) - resolve(b))
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface IEndingOptions {
|
|
2
|
+
unite?: (quantity: number, word: string) => string;
|
|
3
|
+
}
|
|
4
|
+
export declare function ending(quantity: number, base: string, multiple: string, alone: string, double: string, options?: IEndingOptions): string | number;
|
|
5
|
+
export declare enum GrammaticGender {
|
|
6
|
+
He = 0,
|
|
7
|
+
She = 1,
|
|
8
|
+
It = 2
|
|
9
|
+
}
|
|
10
|
+
export declare function pluralization_of_numeric(quantity: number, grammatic_gender: GrammaticGender, pattern: string[]): string | number;
|
|
11
|
+
export declare function pluralize_numeric_form(quantity: number, grammatic_gender: GrammaticGender): string;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/primitives/pluralization/index.ts"],"names":[],"mappings":"AACA,UAAU,cAAc;IACvB,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACnD;AACD,wBAAgB,MAAM,CACrB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,cAAmB,mBAqB5B;AAGD,oBAAY,eAAe;IAC1B,EAAE,IAAI;IACN,GAAG,IAAI;IACP,EAAE,IAAI;CACN;AACD,wBAAgB,wBAAwB,CACvC,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,eAAe,EACjC,OAAO,EAAE,MAAM,EAAE,mBASjB;AAED,wBAAgB,sBAAsB,CACrC,QAAQ,EAAE,MAAM,EAChB,gBAAgB,EAAE,eAAe,GAC/B,MAAM,CA0FR"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
export function ending(quantity, base, multiple, alone, double, options = {}) {
|
|
2
|
+
if (Number.isNaN(+quantity)) {
|
|
3
|
+
return Number.NaN;
|
|
4
|
+
}
|
|
5
|
+
// Закономерность чисел 10-20, 110-120...
|
|
6
|
+
const _clean_quantity = quantity % 100 < 20 ? quantity % 20 : quantity % 10;
|
|
7
|
+
const end = _clean_quantity >= 5 || _clean_quantity === 0
|
|
8
|
+
? multiple
|
|
9
|
+
: _clean_quantity > 1
|
|
10
|
+
? double
|
|
11
|
+
: alone;
|
|
12
|
+
const word = base + end;
|
|
13
|
+
options.unite ||= (quantity, word) => `${quantity} ${word}`;
|
|
14
|
+
const input = options.unite(quantity, word);
|
|
15
|
+
return input;
|
|
16
|
+
}
|
|
17
|
+
// MARK: Plularization
|
|
18
|
+
export var GrammaticGender;
|
|
19
|
+
(function (GrammaticGender) {
|
|
20
|
+
GrammaticGender[GrammaticGender["He"] = 0] = "He";
|
|
21
|
+
GrammaticGender[GrammaticGender["She"] = 1] = "She";
|
|
22
|
+
GrammaticGender[GrammaticGender["It"] = 2] = "It";
|
|
23
|
+
})(GrammaticGender || (GrammaticGender = {}));
|
|
24
|
+
export function pluralization_of_numeric(quantity, grammatic_gender, pattern) {
|
|
25
|
+
return ending(Math.abs(quantity), "", pattern[0], pattern[1], pattern[2], {
|
|
26
|
+
unite: (_, word) => word.replace("{}", pluralize_numeric_form(quantity, grammatic_gender)),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export function pluralize_numeric_form(quantity, grammatic_gender) {
|
|
30
|
+
if (quantity === 0) {
|
|
31
|
+
return "ноль";
|
|
32
|
+
}
|
|
33
|
+
let _result = "";
|
|
34
|
+
if (quantity < 0) {
|
|
35
|
+
quantity = -quantity;
|
|
36
|
+
_result += "минус ";
|
|
37
|
+
}
|
|
38
|
+
if (quantity >= 1_000_000)
|
|
39
|
+
return String(quantity);
|
|
40
|
+
if (quantity >= 1_000) {
|
|
41
|
+
const x = Math.floor(quantity / 1_000);
|
|
42
|
+
_result += ending(x, "тысяч", "", "а", "и", {
|
|
43
|
+
unite: (_, word) => `${count_of_bit(x, GrammaticGender.She)} ${word} `,
|
|
44
|
+
});
|
|
45
|
+
quantity -= x * 1_000;
|
|
46
|
+
}
|
|
47
|
+
if (quantity >= 1) {
|
|
48
|
+
_result += `${count_of_bit(quantity, grammatic_gender)} `;
|
|
49
|
+
}
|
|
50
|
+
return _result.trimEnd();
|
|
51
|
+
function count_of_bit(value, grammatic_gender) {
|
|
52
|
+
let _result = "";
|
|
53
|
+
if (value >= 100) {
|
|
54
|
+
const x = Math.floor(value / 100);
|
|
55
|
+
_result += `${[
|
|
56
|
+
"сто",
|
|
57
|
+
"двести",
|
|
58
|
+
"триста",
|
|
59
|
+
"четыреста",
|
|
60
|
+
"пятьсот",
|
|
61
|
+
"шестьсот",
|
|
62
|
+
"семьсот",
|
|
63
|
+
"восемьсот",
|
|
64
|
+
"девятьсот",
|
|
65
|
+
][x - 1]} `;
|
|
66
|
+
value -= x * 100;
|
|
67
|
+
}
|
|
68
|
+
if (value >= 20) {
|
|
69
|
+
const x = Math.floor(value / 10);
|
|
70
|
+
_result += `${[
|
|
71
|
+
"двадцать",
|
|
72
|
+
"тридцать",
|
|
73
|
+
"сорок",
|
|
74
|
+
"пятьдесят",
|
|
75
|
+
"шестдесят",
|
|
76
|
+
"семдесят",
|
|
77
|
+
"восемьдесят",
|
|
78
|
+
"девяносто",
|
|
79
|
+
][x - 2]} `;
|
|
80
|
+
value -= x * 10;
|
|
81
|
+
}
|
|
82
|
+
if (value >= 1) {
|
|
83
|
+
_result += [
|
|
84
|
+
["один", "одна", "одно"][grammatic_gender],
|
|
85
|
+
["два", "две", "два"][grammatic_gender],
|
|
86
|
+
"три",
|
|
87
|
+
"четыре",
|
|
88
|
+
"пять",
|
|
89
|
+
"шесть",
|
|
90
|
+
"семь",
|
|
91
|
+
"восемь",
|
|
92
|
+
"девять",
|
|
93
|
+
"десять",
|
|
94
|
+
"одинадцать",
|
|
95
|
+
"двенадцать",
|
|
96
|
+
"тринадцать",
|
|
97
|
+
"четырнадцать",
|
|
98
|
+
"пятьнадцать",
|
|
99
|
+
"шестнадцать",
|
|
100
|
+
"семнадцать",
|
|
101
|
+
"восемнадцать",
|
|
102
|
+
"девятнадцать",
|
|
103
|
+
][value - 1];
|
|
104
|
+
}
|
|
105
|
+
return _result.trimEnd();
|
|
106
|
+
}
|
|
107
|
+
}
|