@ix-xs/node-comfort 2.0.0 → 2.1.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/CHANGELOG.md +150 -0
- package/README.md +32 -30
- package/index.js +3 -3
- package/package.json +8 -7
- package/src/Arr.js +222 -58
- package/src/Async.js +322 -90
- package/src/Cache.js +148 -44
- package/src/Checker.js +257 -26
- package/src/Cli.js +404 -145
- package/src/Color.js +224 -83
- package/src/Crypto.js +481 -106
- package/src/Emitter.js +104 -37
- package/src/Env.js +176 -42
- package/src/FS.js +203 -60
- package/src/Func.js +226 -76
- package/src/Http.js +311 -149
- package/src/Id.js +281 -96
- package/src/Logger.js +302 -78
- package/src/Num.js +182 -60
- package/src/Obj.js +340 -125
- package/src/SQLite.js +426 -105
- package/src/Schema.js +602 -87
- package/src/Str.js +316 -86
- package/src/Sys.js +197 -54
- package/src/Time.js +572 -317
- package/src/Utils.js +62 -41
- package/src/errors.js +54 -11
- package/src/internal/shared.js +415 -0
- package/types/index.d.ts +59 -17
- package/types/src/Arr.d.ts +78 -0
- package/types/src/Arr.d.ts.map +1 -1
- package/types/src/Async.d.ts +52 -1
- package/types/src/Async.d.ts.map +1 -1
- package/types/src/Cache.d.ts +33 -7
- package/types/src/Cache.d.ts.map +1 -1
- package/types/src/Checker.d.ts +155 -2
- package/types/src/Checker.d.ts.map +1 -1
- package/types/src/Cli.d.ts +97 -15
- package/types/src/Cli.d.ts.map +1 -1
- package/types/src/Color.d.ts +7 -1
- package/types/src/Color.d.ts.map +1 -1
- package/types/src/Crypto.d.ts +99 -13
- package/types/src/Crypto.d.ts.map +1 -1
- package/types/src/Emitter.d.ts +36 -17
- package/types/src/Emitter.d.ts.map +1 -1
- package/types/src/Env.d.ts +31 -1
- package/types/src/Env.d.ts.map +1 -1
- package/types/src/FS.d.ts +36 -5
- package/types/src/FS.d.ts.map +1 -1
- package/types/src/Func.d.ts +147 -19
- package/types/src/Func.d.ts.map +1 -1
- package/types/src/Http.d.ts +113 -43
- package/types/src/Http.d.ts.map +1 -1
- package/types/src/Id.d.ts +60 -8
- package/types/src/Id.d.ts.map +1 -1
- package/types/src/Logger.d.ts +53 -2
- package/types/src/Logger.d.ts.map +1 -1
- package/types/src/Num.d.ts +48 -7
- package/types/src/Num.d.ts.map +1 -1
- package/types/src/Obj.d.ts +67 -4
- package/types/src/Obj.d.ts.map +1 -1
- package/types/src/SQLite.d.ts +123 -14
- package/types/src/SQLite.d.ts.map +1 -1
- package/types/src/Schema.d.ts +256 -7
- package/types/src/Schema.d.ts.map +1 -1
- package/types/src/Str.d.ts +49 -1
- package/types/src/Str.d.ts.map +1 -1
- package/types/src/Sys.d.ts +51 -6
- package/types/src/Sys.d.ts.map +1 -1
- package/types/src/Time.d.ts +130 -34
- package/types/src/Time.d.ts.map +1 -1
- package/types/src/Utils.d.ts +15 -11
- package/types/src/Utils.d.ts.map +1 -1
- package/types/src/errors.d.ts +27 -3
- package/types/src/errors.d.ts.map +1 -1
- package/types/src/internal/shared.d.ts +123 -0
- package/types/src/internal/shared.d.ts.map +1 -0
package/src/Num.js
CHANGED
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
* num.percentile(latencies, 95);
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
const { intl, preciseSum } = require("./internal/shared.js");
|
|
17
|
+
|
|
16
18
|
/** The default language, changed with `setLocale()`. */
|
|
17
19
|
const _config = { locale: "en" };
|
|
18
20
|
|
|
@@ -88,9 +90,32 @@ const _shift = (value, exp) => {
|
|
|
88
90
|
*/
|
|
89
91
|
const _roundWith = (value, decimals, fn) => {
|
|
90
92
|
if (!Number.isFinite(value)) return value;
|
|
93
|
+
if (decimals === 0) return fn(value);
|
|
94
|
+
if (decimals > 0 && Number.isInteger(value)) return value;
|
|
91
95
|
return _shift(fn(_shift(value, decimals)), -decimals);
|
|
92
96
|
};
|
|
93
97
|
|
|
98
|
+
/**
|
|
99
|
+
* How many decimals a number is written with, exponent notation included.
|
|
100
|
+
* @param {number} n
|
|
101
|
+
* @returns {number}
|
|
102
|
+
*/
|
|
103
|
+
const _decimalsOf = (n) => {
|
|
104
|
+
if (Number.isInteger(n) || !Number.isFinite(n)) return 0;
|
|
105
|
+
const text = String(n);
|
|
106
|
+
const exponent = text.indexOf("e-");
|
|
107
|
+
if (exponent === -1) return text.length - text.indexOf(".") - 1;
|
|
108
|
+
const mantissa = text.slice(0, exponent);
|
|
109
|
+
return Number(text.slice(exponent + 2)) + (mantissa.includes(".") ? mantissa.length - mantissa.indexOf(".") - 1 : 0);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/** @param {number} n */
|
|
113
|
+
const _halfAwayFromZero = (n) => Math.sign(n) * Math.round(Math.abs(n)) || 0;
|
|
114
|
+
|
|
115
|
+
/** @type {Array<[number, string]>} */
|
|
116
|
+
const _ABBREVIATIONS = [[1e12, "T"], [1e9, "B"], [1e6, "M"], [1e3, "K"]];
|
|
117
|
+
const _ORDINAL = { type: "ordinal" };
|
|
118
|
+
|
|
94
119
|
const _SIZE_UNITS = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
95
120
|
const _IEC_UNITS = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"];
|
|
96
121
|
|
|
@@ -170,7 +195,7 @@ function wrap(value, min, max) {
|
|
|
170
195
|
* @returns {number}
|
|
171
196
|
*/
|
|
172
197
|
function round(value, decimals = 0) {
|
|
173
|
-
return _roundWith(value, decimals,
|
|
198
|
+
return _roundWith(value, decimals, _halfAwayFromZero);
|
|
174
199
|
}
|
|
175
200
|
|
|
176
201
|
/**
|
|
@@ -214,8 +239,7 @@ function ceil(value, decimals = 0) {
|
|
|
214
239
|
*/
|
|
215
240
|
function snap(value, step) {
|
|
216
241
|
if (!step) return value;
|
|
217
|
-
|
|
218
|
-
return round(Math.round(value / step) * step, decimals);
|
|
242
|
+
return round(Math.round(value / step) * step, _decimalsOf(step));
|
|
219
243
|
}
|
|
220
244
|
|
|
221
245
|
/**
|
|
@@ -302,17 +326,31 @@ function randomInt(min, max) {
|
|
|
302
326
|
}
|
|
303
327
|
|
|
304
328
|
/**
|
|
305
|
-
* Adds up numbers.
|
|
329
|
+
* Adds up numbers, without the drift of a naive loop.
|
|
306
330
|
*
|
|
307
331
|
* @example
|
|
308
|
-
* num.sum([1, 2, 3]);
|
|
332
|
+
* num.sum([1, 2, 3]); // 6
|
|
333
|
+
* num.sum([0.1, 0.2, 0.3]); // 0.6, where a plain loop gives 0.6000000000000001
|
|
309
334
|
*
|
|
310
335
|
* @param {Iterable<number>} values
|
|
311
336
|
* @returns {number}
|
|
312
337
|
*/
|
|
313
338
|
function sum(values) {
|
|
314
|
-
|
|
315
|
-
|
|
339
|
+
return preciseSum(values);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Multiplies numbers. An empty list gives `1`.
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
* num.product([2, 3, 4]); // 24
|
|
347
|
+
*
|
|
348
|
+
* @param {Iterable<number>} values
|
|
349
|
+
* @returns {number}
|
|
350
|
+
*/
|
|
351
|
+
function product(values) {
|
|
352
|
+
let total = 1;
|
|
353
|
+
for (const n of values) total *= n;
|
|
316
354
|
return total;
|
|
317
355
|
}
|
|
318
356
|
|
|
@@ -326,8 +364,8 @@ function sum(values) {
|
|
|
326
364
|
* @returns {number}
|
|
327
365
|
*/
|
|
328
366
|
function average(values) {
|
|
329
|
-
const list = [...values];
|
|
330
|
-
return list.length ?
|
|
367
|
+
const list = Array.isArray(values) ? values : [...values];
|
|
368
|
+
return list.length ? preciseSum(list) / list.length : 0;
|
|
331
369
|
}
|
|
332
370
|
|
|
333
371
|
/**
|
|
@@ -359,9 +397,16 @@ function median(values) {
|
|
|
359
397
|
function mode(values) {
|
|
360
398
|
/** @type {Map<number, number>} */
|
|
361
399
|
const counts = new Map();
|
|
362
|
-
|
|
363
|
-
const
|
|
364
|
-
|
|
400
|
+
let best = 0;
|
|
401
|
+
for (const n of values) {
|
|
402
|
+
const count = (counts.get(n) ?? 0) + 1;
|
|
403
|
+
counts.set(n, count);
|
|
404
|
+
if (count > best) best = count;
|
|
405
|
+
}
|
|
406
|
+
/** @type {number[]} */
|
|
407
|
+
const out = [];
|
|
408
|
+
for (const [n, count] of counts) if (count === best) out.push(n);
|
|
409
|
+
return out;
|
|
365
410
|
}
|
|
366
411
|
|
|
367
412
|
/**
|
|
@@ -375,11 +420,13 @@ function mode(values) {
|
|
|
375
420
|
* @returns {number}
|
|
376
421
|
*/
|
|
377
422
|
function variance(values, options = {}) {
|
|
378
|
-
const list = [...values];
|
|
423
|
+
const list = Array.isArray(values) ? values : [...values];
|
|
379
424
|
const divisor = options.sample ? list.length - 1 : list.length;
|
|
380
425
|
if (divisor <= 0) return 0;
|
|
381
|
-
const mean =
|
|
382
|
-
|
|
426
|
+
const mean = preciseSum(list) / list.length;
|
|
427
|
+
let total = 0;
|
|
428
|
+
for (let i = 0; i < list.length; i++) total += (list[i] - mean) ** 2;
|
|
429
|
+
return total / divisor;
|
|
383
430
|
}
|
|
384
431
|
|
|
385
432
|
/**
|
|
@@ -419,25 +466,33 @@ function percentile(values, p) {
|
|
|
419
466
|
|
|
420
467
|
/**
|
|
421
468
|
* The smallest number. Unlike `Math.min(...array)`, huge arrays are fine.
|
|
469
|
+
* Like `Math.min`, a `NaN` in the list gives `NaN`.
|
|
422
470
|
*
|
|
423
471
|
* @param {Iterable<number>} values
|
|
424
472
|
* @returns {number | undefined} `undefined` for an empty list.
|
|
425
473
|
*/
|
|
426
474
|
function min(values) {
|
|
427
475
|
let best;
|
|
428
|
-
for (const n of values)
|
|
476
|
+
for (const n of values) {
|
|
477
|
+
if (n !== n) return NaN;
|
|
478
|
+
if (best === undefined || n < best) best = n;
|
|
479
|
+
}
|
|
429
480
|
return best;
|
|
430
481
|
}
|
|
431
482
|
|
|
432
483
|
/**
|
|
433
484
|
* The largest number. Unlike `Math.max(...array)`, huge arrays are fine.
|
|
485
|
+
* Like `Math.max`, a `NaN` in the list gives `NaN`.
|
|
434
486
|
*
|
|
435
487
|
* @param {Iterable<number>} values
|
|
436
488
|
* @returns {number | undefined} `undefined` for an empty list.
|
|
437
489
|
*/
|
|
438
490
|
function max(values) {
|
|
439
491
|
let best;
|
|
440
|
-
for (const n of values)
|
|
492
|
+
for (const n of values) {
|
|
493
|
+
if (n !== n) return NaN;
|
|
494
|
+
if (best === undefined || n > best) best = n;
|
|
495
|
+
}
|
|
441
496
|
return best;
|
|
442
497
|
}
|
|
443
498
|
|
|
@@ -458,6 +513,25 @@ function percent(value, total, decimals = 2) {
|
|
|
458
513
|
return round((value / total) * 100, decimals);
|
|
459
514
|
}
|
|
460
515
|
|
|
516
|
+
/**
|
|
517
|
+
* How much a value changed, in percent. Going from `0` to anything else is
|
|
518
|
+
* an infinite change.
|
|
519
|
+
*
|
|
520
|
+
* @example
|
|
521
|
+
* num.percentChange(80, 100); // 25
|
|
522
|
+
* num.percentChange(100, 80); // -20
|
|
523
|
+
*
|
|
524
|
+
* @param {number} from
|
|
525
|
+
* @param {number} to
|
|
526
|
+
* @param {number} [decimals=2]
|
|
527
|
+
* @returns {number}
|
|
528
|
+
*/
|
|
529
|
+
function percentChange(from, to, decimals = 2) {
|
|
530
|
+
if (from === to) return 0;
|
|
531
|
+
if (!from) return to > 0 ? Infinity : -Infinity;
|
|
532
|
+
return round(((to - from) / Math.abs(from)) * 100, decimals);
|
|
533
|
+
}
|
|
534
|
+
|
|
461
535
|
/**
|
|
462
536
|
* Greatest common divisor.
|
|
463
537
|
*
|
|
@@ -468,12 +542,18 @@ function percent(value, total, decimals = 2) {
|
|
|
468
542
|
* @returns {number}
|
|
469
543
|
*/
|
|
470
544
|
function gcd(...values) {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
let
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
545
|
+
let result = 0;
|
|
546
|
+
for (const value of values) {
|
|
547
|
+
let x = Math.abs(result);
|
|
548
|
+
let y = Math.abs(value);
|
|
549
|
+
while (y) {
|
|
550
|
+
const rest = x % y;
|
|
551
|
+
x = y;
|
|
552
|
+
y = rest;
|
|
553
|
+
}
|
|
554
|
+
result = x;
|
|
555
|
+
}
|
|
556
|
+
return result;
|
|
477
557
|
}
|
|
478
558
|
|
|
479
559
|
/**
|
|
@@ -487,7 +567,8 @@ function gcd(...values) {
|
|
|
487
567
|
*/
|
|
488
568
|
function lcm(...values) {
|
|
489
569
|
if (!values.length) return 0;
|
|
490
|
-
|
|
570
|
+
// Dividing first keeps the intermediate value small.
|
|
571
|
+
return values.reduce((a, b) => (a === 0 || b === 0 ? 0 : Math.abs((a / gcd(a, b)) * b)));
|
|
491
572
|
}
|
|
492
573
|
|
|
493
574
|
/**
|
|
@@ -578,10 +659,13 @@ function range(start, end, step) {
|
|
|
578
659
|
}
|
|
579
660
|
const inc = step ?? (to < from ? -1 : 1);
|
|
580
661
|
if (inc === 0 || (inc > 0 && from > to) || (inc < 0 && from < to)) return [];
|
|
581
|
-
const decimals = Math.max((
|
|
582
|
-
|
|
583
|
-
const count = Math.ceil((to - from) / inc);
|
|
584
|
-
|
|
662
|
+
const decimals = Math.max(_decimalsOf(inc), _decimalsOf(from));
|
|
663
|
+
// Rounding the ratio keeps 1.1 / 0.1 = 11.000000000000002 from adding an item.
|
|
664
|
+
const count = Math.ceil(round((to - from) / inc, 9));
|
|
665
|
+
if (!Number.isFinite(count) || count > 0xffffffff) throw new RangeError("range() would produce too many numbers");
|
|
666
|
+
/** @type {number[]} */
|
|
667
|
+
const out = new Array(count);
|
|
668
|
+
for (let i = 0; i < count; i++) out[i] = decimals ? round(from + i * inc, decimals) : from + i * inc;
|
|
585
669
|
return out;
|
|
586
670
|
}
|
|
587
671
|
|
|
@@ -644,13 +728,22 @@ function formatBytes(bytes, options = {}) {
|
|
|
644
728
|
if (!Number.isFinite(bytes) || bytes === 0) return `0 ${units[0]}`;
|
|
645
729
|
const decimals = options.decimals ?? 1;
|
|
646
730
|
const base = options.binary === false ? 1000 : 1024;
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
731
|
+
const last = units.length - 1;
|
|
732
|
+
let amount = Math.abs(bytes);
|
|
733
|
+
let unit = 0;
|
|
734
|
+
while (amount >= base && unit < last) {
|
|
735
|
+
amount /= base;
|
|
736
|
+
unit++;
|
|
737
|
+
}
|
|
738
|
+
amount = round(amount, decimals);
|
|
739
|
+
// Rounding can reach the next unit: 1023.99 KB reads better as 1 MB.
|
|
740
|
+
if (amount >= base && unit < last) {
|
|
741
|
+
amount = round(amount / base, decimals);
|
|
742
|
+
unit++;
|
|
743
|
+
}
|
|
651
744
|
const locale = options.locale ?? (_config.locale === "en" ? undefined : _config.locale);
|
|
652
|
-
const text = locale ?
|
|
653
|
-
return `${
|
|
745
|
+
const text = locale ? intl.number(locale, { maximumFractionDigits: decimals }).format(amount) : String(amount);
|
|
746
|
+
return `${bytes < 0 ? "-" : ""}${text} ${units[unit]}`;
|
|
654
747
|
}
|
|
655
748
|
|
|
656
749
|
/**
|
|
@@ -670,26 +763,26 @@ function formatBytes(bytes, options = {}) {
|
|
|
670
763
|
*/
|
|
671
764
|
function format(value, options = {}) {
|
|
672
765
|
/** @type {Intl.NumberFormatOptions} */
|
|
673
|
-
const
|
|
766
|
+
const format = {};
|
|
674
767
|
if (options.decimals !== undefined) {
|
|
675
|
-
|
|
676
|
-
|
|
768
|
+
format.minimumFractionDigits = options.decimals;
|
|
769
|
+
format.maximumFractionDigits = options.decimals;
|
|
677
770
|
}
|
|
678
|
-
if (options.minDecimals !== undefined)
|
|
679
|
-
if (options.maxDecimals !== undefined)
|
|
771
|
+
if (options.minDecimals !== undefined) format.minimumFractionDigits = options.minDecimals;
|
|
772
|
+
if (options.maxDecimals !== undefined) format.maximumFractionDigits = options.maxDecimals;
|
|
680
773
|
if (options.compact) {
|
|
681
|
-
|
|
682
|
-
|
|
774
|
+
format.notation = "compact";
|
|
775
|
+
format.compactDisplay = "short";
|
|
683
776
|
}
|
|
684
|
-
if (options.style === "percent")
|
|
777
|
+
if (options.style === "percent") format.style = "percent";
|
|
685
778
|
if (options.unit) {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
779
|
+
format.style = "unit";
|
|
780
|
+
format.unit = options.unit;
|
|
781
|
+
format.unitDisplay = options.unitDisplay ?? "short";
|
|
689
782
|
}
|
|
690
|
-
if (options.signDisplay)
|
|
691
|
-
if (options.grouping === false)
|
|
692
|
-
return
|
|
783
|
+
if (options.signDisplay) format.signDisplay = options.signDisplay;
|
|
784
|
+
if (options.grouping === false) format.useGrouping = false;
|
|
785
|
+
return intl.number(options.locale ?? _config.locale, format).format(value);
|
|
693
786
|
}
|
|
694
787
|
|
|
695
788
|
/**
|
|
@@ -707,13 +800,13 @@ function format(value, options = {}) {
|
|
|
707
800
|
*/
|
|
708
801
|
function currency(value, code, options = {}) {
|
|
709
802
|
/** @type {Intl.NumberFormatOptions} */
|
|
710
|
-
const
|
|
803
|
+
const format = { style: "currency", currency: code, currencyDisplay: options.display ?? "symbol" };
|
|
711
804
|
if (options.decimals !== undefined) {
|
|
712
|
-
|
|
713
|
-
|
|
805
|
+
format.minimumFractionDigits = options.decimals;
|
|
806
|
+
format.maximumFractionDigits = options.decimals;
|
|
714
807
|
}
|
|
715
|
-
if (options.accounting)
|
|
716
|
-
return
|
|
808
|
+
if (options.accounting) format.currencySign = "accounting";
|
|
809
|
+
return intl.number(options.locale ?? _config.locale, format).format(value);
|
|
717
810
|
}
|
|
718
811
|
|
|
719
812
|
/**
|
|
@@ -729,12 +822,19 @@ function currency(value, code, options = {}) {
|
|
|
729
822
|
* @returns {string}
|
|
730
823
|
*/
|
|
731
824
|
function abbreviate(value, decimals = 1) {
|
|
825
|
+
if (!Number.isFinite(value)) return String(value);
|
|
732
826
|
const sign = value < 0 ? "-" : "";
|
|
733
827
|
const abs = Math.abs(value);
|
|
734
|
-
for (
|
|
735
|
-
|
|
828
|
+
for (let i = 0; i < _ABBREVIATIONS.length; i++) {
|
|
829
|
+
const [limit, suffix] = _ABBREVIATIONS[i];
|
|
830
|
+
if (abs < limit) continue;
|
|
831
|
+
const rounded = round(abs / limit, decimals);
|
|
832
|
+
// Rounding can reach the next suffix: 999,950 is 1M, not 1000K.
|
|
833
|
+
if (rounded >= 1000 && i > 0) return `${sign}${round(abs / _ABBREVIATIONS[i - 1][0], decimals)}${_ABBREVIATIONS[i - 1][1]}`;
|
|
834
|
+
return `${sign}${rounded}${suffix}`;
|
|
736
835
|
}
|
|
737
|
-
|
|
836
|
+
const rounded = round(abs, decimals);
|
|
837
|
+
return rounded >= 1000 ? `${sign}${round(abs / 1000, decimals)}K` : `${sign}${rounded}`;
|
|
738
838
|
}
|
|
739
839
|
|
|
740
840
|
/**
|
|
@@ -753,6 +853,25 @@ function thousands(value, separator = ",") {
|
|
|
753
853
|
return dec === undefined ? grouped : `${grouped}.${dec}`;
|
|
754
854
|
}
|
|
755
855
|
|
|
856
|
+
/**
|
|
857
|
+
* Pads the integer part with zeros (or another character).
|
|
858
|
+
*
|
|
859
|
+
* @example
|
|
860
|
+
* num.pad(7, 3); // "007"
|
|
861
|
+
* num.pad(-7, 3); // "-007"
|
|
862
|
+
* num.pad(3.5, 2); // "03.5"
|
|
863
|
+
*
|
|
864
|
+
* @param {number} value
|
|
865
|
+
* @param {number} length Digits wanted before the decimal point.
|
|
866
|
+
* @param {string} [char="0"]
|
|
867
|
+
* @returns {string}
|
|
868
|
+
*/
|
|
869
|
+
function pad(value, length, char = "0") {
|
|
870
|
+
const [int, dec] = String(Math.abs(value)).split(".");
|
|
871
|
+
const sign = value < 0 || Object.is(value, -0) ? "-" : "";
|
|
872
|
+
return `${sign}${int.padStart(length, char)}${dec === undefined ? "" : `.${dec}`}`;
|
|
873
|
+
}
|
|
874
|
+
|
|
756
875
|
/**
|
|
757
876
|
* Adds the ordinal suffix: 1st, 2nd, 3rd... in several languages.
|
|
758
877
|
*
|
|
@@ -771,16 +890,16 @@ function ordinal(value, options = {}) {
|
|
|
771
890
|
const suffixes = options.suffixes ?? _ORDINAL_SUFFIXES[language] ?? _ORDINAL_SUFFIXES.en;
|
|
772
891
|
let category;
|
|
773
892
|
try {
|
|
774
|
-
category =
|
|
893
|
+
category = intl.plural(locale, _ORDINAL).select(value);
|
|
775
894
|
} catch {
|
|
776
|
-
category =
|
|
895
|
+
category = intl.plural("en", _ORDINAL).select(value);
|
|
777
896
|
}
|
|
778
897
|
return `${value}${suffixes[category] ?? suffixes.other ?? ""}`;
|
|
779
898
|
}
|
|
780
899
|
|
|
781
900
|
/**
|
|
782
|
-
* Sets the default language of `format`, `currency`, `
|
|
783
|
-
* `
|
|
901
|
+
* Sets the default language of `format`, `currency`, `ordinal` and
|
|
902
|
+
* `formatBytes`.
|
|
784
903
|
*
|
|
785
904
|
* @example
|
|
786
905
|
* num.setLocale("fr");
|
|
@@ -822,6 +941,7 @@ module.exports = {
|
|
|
822
941
|
random,
|
|
823
942
|
randomInt,
|
|
824
943
|
sum,
|
|
944
|
+
product,
|
|
825
945
|
average,
|
|
826
946
|
median,
|
|
827
947
|
mode,
|
|
@@ -831,6 +951,7 @@ module.exports = {
|
|
|
831
951
|
min,
|
|
832
952
|
max,
|
|
833
953
|
percent,
|
|
954
|
+
percentChange,
|
|
834
955
|
gcd,
|
|
835
956
|
lcm,
|
|
836
957
|
isPrime,
|
|
@@ -845,5 +966,6 @@ module.exports = {
|
|
|
845
966
|
currency,
|
|
846
967
|
abbreviate,
|
|
847
968
|
thousands,
|
|
969
|
+
pad,
|
|
848
970
|
ordinal,
|
|
849
971
|
};
|