foreslash 0.3.2 → 0.3.3

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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # Change Log
2
2
 
3
+ ## Version 0.3.3 - 2025-11-05
4
+
5
+ Unstable version
6
+
7
+ - Feat 🥥 Functions added: `lerp` `scientificNotation` `castArray` `romanNumerals` `randomDistribution` and so forth
8
+ - Fix 🥕 Document: Added version tags to `memo` and description to several methods
9
+ - Other fixes and improvements
10
+
11
+ 不稳定版本
12
+
13
+ - 功能 🥥 添加函数: `lerp` `scientificNotation` `castArray` `randomDistribution` 等
14
+ - 修复 🥕 文档: 补充 `memo` 的版本标签和部分方法的说明
15
+ - 其他修复与优化
16
+
3
17
  ## Version 0.3.2 - 2025-09-23
4
18
 
5
19
  Unstable version
package/lib/index.cmn.cjs CHANGED
@@ -45,6 +45,10 @@ function cartesianProduct(...arrList) {
45
45
  return res;
46
46
  }
47
47
 
48
+ function castArray(value) {
49
+ return Array.isArray(value) ? value.slice() : [value];
50
+ }
51
+
48
52
  function chunk(array, size = 2) {
49
53
  const res = [];
50
54
  if (size < 1 || isNaN(size))
@@ -609,6 +613,201 @@ function format(num, options) {
609
613
  return sign + integer;
610
614
  }
611
615
 
616
+ function lerp(val1, val2, t) {
617
+ if (isNumber(val1)) {
618
+ if (!isNumber(val2))
619
+ throw new Error('Invalid val2 parameter: val2 should be a number');
620
+ return _lerp(val1, val2, t);
621
+ }
622
+ else if (isArray(val1)) {
623
+ if (!isArray(val2))
624
+ throw new Error('Invalid val2 parameter: val2 should be an Array');
625
+ if (val1.length !== val2.length) {
626
+ throw new Error(`Invalid val2 parameter: the length of val2 (${val2.length}) should be consistent with val1 (${val1.length})`);
627
+ }
628
+ if (val1.length === 0)
629
+ return [];
630
+ if (isNumber(val1[0])) {
631
+ if (!isNumber(val2[0]))
632
+ throw new Error('Invalid val2 parameter: val2 should be an Array<number>');
633
+ return val1.map((v1, index) => _lerp(v1, val2[index], t));
634
+ }
635
+ else if (isArray(val1[0])) {
636
+ const res = [];
637
+ for (let i = 0; i < val1.length; i++) {
638
+ const arr1 = val1[i];
639
+ const arr2 = val2[i];
640
+ if (arr1.length !== arr2.length) {
641
+ throw new Error(`Invalid val2 parameter: The length of array at index ${i} in val2 (${arr2.length}) must match the length of the corresponding array in val1 (${arr1.length})`);
642
+ }
643
+ res.push(arr1.map((v1, index) => _lerp(v1, arr2[index], t)));
644
+ }
645
+ return res;
646
+ }
647
+ }
648
+ throw new Error('Invalid val1 parameter: val1 should be a number or Array');
649
+ }
650
+ function _lerp(val1, val2, t) {
651
+ return val1 + (val2 - val1) * t;
652
+ }
653
+
654
+ function romanNumerals(num, options) {
655
+ const str = decimalNotation(num);
656
+ if (/NaN|Inf/.test(str))
657
+ return str;
658
+ const type = (options || {}).type || 'unicode';
659
+ const thousand = (options || {}).thousand || 'normal';
660
+ const forceSplitThousand = thousand === 'strict';
661
+ const integer = str.split('.')[0].replace('-', '');
662
+ if (integer === '0') {
663
+ const zero = (options || {}).zero || '0';
664
+ if (type === 'json')
665
+ return `["${zero}"]`;
666
+ else
667
+ return zero;
668
+ }
669
+ if (integer === '1') {
670
+ const one = (options || {}).one || 'I';
671
+ if (type === 'json')
672
+ return `["${one}"]`;
673
+ else
674
+ return one;
675
+ }
676
+ const chunks = chunk(Array.from(integer).reverse(), 3);
677
+ const romanChunks = [];
678
+ for (let i = 0; i < chunks.length; i++) {
679
+ const val = Number(chunks[i].reverse().join(''));
680
+ if (i === chunks.length - 2 && !forceSplitThousand && Number(chunks[i + 1]) < 4) {
681
+ romanChunks.push(number2roman(val + Number(chunks[i + 1]) * 1000));
682
+ break;
683
+ }
684
+ else {
685
+ romanChunks.push(number2roman(val));
686
+ }
687
+ }
688
+ switch (type) {
689
+ case 'js':
690
+ return romanChunks
691
+ .map((str, index) => str
692
+ .split('')
693
+ .map((s) => s + '\\\\u0305'.repeat(index))
694
+ .join(''))
695
+ .reverse()
696
+ .join('');
697
+ case 'html':
698
+ return romanChunks
699
+ .map((str, index) => str
700
+ .split('')
701
+ .map((s) => s + '&#x0305;'.repeat(index))
702
+ .join(''))
703
+ .reverse()
704
+ .join('');
705
+ case 'json':
706
+ return `[${romanChunks.map((str) => `"${str}"`).join(', ')}]`;
707
+ case 'unicode':
708
+ default:
709
+ return romanChunks
710
+ .map((str, index) => str
711
+ .split('')
712
+ .map((s) => s + '\u0305'.repeat(index))
713
+ .join(''))
714
+ .reverse()
715
+ .join('');
716
+ }
717
+ }
718
+ function number2roman(num) {
719
+ const symbols = [
720
+ [1000, 'M'],
721
+ [900, 'CM'],
722
+ [500, 'D'],
723
+ [400, 'CD'],
724
+ [100, 'C'],
725
+ [90, 'XC'],
726
+ [50, 'L'],
727
+ [40, 'XL'],
728
+ [10, 'X'],
729
+ [9, 'IX'],
730
+ [5, 'V'],
731
+ [4, 'IV'],
732
+ [1, 'I'],
733
+ ];
734
+ let roman = '';
735
+ for (const [val, str] of symbols) {
736
+ while (num >= val) {
737
+ num -= val;
738
+ roman += str;
739
+ }
740
+ if (num == 0)
741
+ break;
742
+ }
743
+ return roman;
744
+ }
745
+
746
+ function scientificNotation(num, options) {
747
+ const str = decimalNotation(num);
748
+ if (/NaN|Inf/.test(str))
749
+ return str;
750
+ const type = (options || {}).type || 'unicode';
751
+ const _precision = (options || {}).precision;
752
+ const precision = isNumber(_precision) ? clamp(_precision !== null && _precision !== void 0 ? _precision : 2, 0, Infinity) : null;
753
+ const round = (options || {}).round || 'round';
754
+ let [integer, fractional] = str.split('.');
755
+ let sign = '';
756
+ if (/^-/.test(integer)) {
757
+ integer = integer.substring(1);
758
+ sign = '-';
759
+ }
760
+ fractional = fractional !== null && fractional !== void 0 ? fractional : '';
761
+ let exp = 0;
762
+ let n = '';
763
+ if (integer === '0') {
764
+ exp = /^(0+)/.test(fractional) ? -(fractional.match(/^(0+)/)[0].length + 1) : -1;
765
+ [integer, fractional] = [fractional.slice(-exp - 1, -exp), fractional.slice(-exp)];
766
+ }
767
+ else {
768
+ exp = integer.length - 1;
769
+ [integer, fractional] = [integer.slice(0, 1), integer.slice(1) + fractional];
770
+ }
771
+ if (isNumber(precision)) {
772
+ if (fractional.length > precision) {
773
+ const roundMap = { round: roundBase, banker: roundBank, floor: roundFloor, ceil: roundCeil };
774
+ [integer, fractional] = (roundMap[round] || roundBase)(integer, fractional, precision, !!sign);
775
+ }
776
+ else if (fractional.length < precision) {
777
+ fractional += '0'.repeat(precision - fractional.length);
778
+ }
779
+ }
780
+ else {
781
+ fractional = fractional.replace(/0+$/, '');
782
+ }
783
+ if ((precision === null && fractional) || (isNumber(precision) && precision > 0)) {
784
+ n = sign + integer + '.' + fractional;
785
+ }
786
+ else
787
+ n = sign + integer;
788
+ switch (type) {
789
+ case 'exp':
790
+ return `${n}e${exp < 0 ? '' : '+'}${exp}`;
791
+ case 'js':
792
+ return `${n}*10**${exp}`;
793
+ case 'code':
794
+ return `${n}*10^${exp}`;
795
+ case 'html':
796
+ return `${n}&#x00d7;10<sup>${exp}</sup>`;
797
+ case 'json':
798
+ return `{"number":"${n}","exp":${exp}}`;
799
+ case 'unicode':
800
+ default:
801
+ return `${n}×10${transferNumberToUniCode(String(exp))}`;
802
+ }
803
+ }
804
+ function transferNumberToUniCode(n) {
805
+ const strMap = Object.assign(Object.assign({}, Array.from('⁰¹²³⁴⁵⁶⁷⁸⁹')), { '-': '⁻', '+': '⁺' });
806
+ return Array.from(n)
807
+ .map((s) => (strMap[s] ? strMap[s] : s))
808
+ .join('');
809
+ }
810
+
612
811
  function parallel(args, fn, options) {
613
812
  return __awaiter(this, void 0, void 0, function* () {
614
813
  if (!args.length)
@@ -1240,6 +1439,54 @@ function randomChoice(arr, weights) {
1240
1439
  return arr[index];
1241
1440
  }
1242
1441
 
1442
+ function randomDistribution(p, options) {
1443
+ const threshold = (options === null || options === void 0 ? void 0 : options.threshold) ? Math.max(options.threshold, Number.MIN_VALUE * 2) : 5e-6;
1444
+ const C = getInitP(p, threshold);
1445
+ let current = C;
1446
+ return function getRandomDistribution() {
1447
+ const res = Math.random() < current;
1448
+ if (res)
1449
+ current = C;
1450
+ else
1451
+ current += C;
1452
+ return res;
1453
+ };
1454
+ }
1455
+ function getInitP(targetP, threshold = 5e-6) {
1456
+ if (targetP <= 0)
1457
+ return 0;
1458
+ if (targetP >= 1)
1459
+ return 1;
1460
+ let [down, up] = [0, 1];
1461
+ let mid = 1;
1462
+ let tempP = 1;
1463
+ let tempPLast = 1;
1464
+ let step = 64;
1465
+ while (step-- > 0) {
1466
+ mid = (down + up) / 2;
1467
+ tempP = getRealPFromInitP(mid);
1468
+ if (Math.abs(tempPLast - tempP) < threshold)
1469
+ break;
1470
+ if (tempP > targetP)
1471
+ up = mid;
1472
+ else
1473
+ down = mid;
1474
+ }
1475
+ return mid;
1476
+ }
1477
+ function getRealPFromInitP(initP) {
1478
+ let sum = 0;
1479
+ let prod = 0;
1480
+ let cur = 0;
1481
+ let max = Math.ceil(1 / initP);
1482
+ for (let n = 1; n <= max; n++) {
1483
+ cur = Math.min(1, n * initP) * (1 - prod);
1484
+ prod += cur;
1485
+ sum += n * cur;
1486
+ }
1487
+ return 1 / sum;
1488
+ }
1489
+
1243
1490
  const radix32 = '0123456789abcdefghijklmnopqrstuv';
1244
1491
  const base32Chars = 'abcdefghijklmnopqrstuvwxyz234567';
1245
1492
  const base32Crockford = '0123456789abcdefghjkmnpqrstvwxyz';
@@ -1866,22 +2113,22 @@ function _deepClone(obj, map, options) {
1866
2113
  if (isFormData(obj))
1867
2114
  return _cloneFormData(obj, map, _deepClone, options);
1868
2115
  let res;
1869
- if (obj instanceof Date) {
2116
+ if (isDate(obj)) {
1870
2117
  res = new Date(obj.valueOf());
1871
2118
  map.set(obj, res);
1872
2119
  }
1873
- else if (obj instanceof RegExp) {
2120
+ else if (isRegExp(obj)) {
1874
2121
  res = new RegExp(obj.source, obj.flags);
1875
2122
  map.set(obj, res);
1876
2123
  }
1877
- else if (obj instanceof ArrayBuffer) {
2124
+ else if (isArrayBuffer(obj)) {
1878
2125
  res = _cloneArrayBuffer(obj, map);
1879
2126
  }
1880
2127
  else if (isTypedArray(obj)) {
1881
2128
  res = new obj.constructor(_cloneArrayBuffer(obj.buffer, map), obj.byteOffset, obj.length);
1882
2129
  map.set(obj, res);
1883
2130
  }
1884
- else if (obj instanceof DataView) {
2131
+ else if (isDataView(obj)) {
1885
2132
  res = new DataView(map.has(obj.buffer) ? map.get(obj.buffer) : _cloneArrayBuffer(obj.buffer, map), obj.byteOffset, obj.byteLength);
1886
2133
  map.set(obj, res);
1887
2134
  }
@@ -2289,6 +2536,7 @@ exports.caseConvert = caseConvert;
2289
2536
  exports.caseKebab = caseKebab;
2290
2537
  exports.casePascal = casePascal;
2291
2538
  exports.caseSnake = caseSnake;
2539
+ exports.castArray = castArray;
2292
2540
  exports.chunk = chunk;
2293
2541
  exports.clamp = clamp;
2294
2542
  exports.compose = compose;
@@ -2304,6 +2552,7 @@ exports.format = format;
2304
2552
  exports.getAcceptableExtByMIME = getAcceptableExtByMIME;
2305
2553
  exports.getAcceptableMIMEByExt = getAcceptableMIMEByExt;
2306
2554
  exports.getGlobalThis = getGlobalThis;
2555
+ exports.getInitP = getInitP;
2307
2556
  exports.getTag = getTag;
2308
2557
  exports.indent = indent;
2309
2558
  exports.isArray = isArray;
@@ -2360,6 +2609,7 @@ exports.isWrapperObject = isWrapperObject;
2360
2609
  exports.isWrapperString = isWrapperString;
2361
2610
  exports.isWrapperSymbol = isWrapperSymbol;
2362
2611
  exports.kebabCase = kebabCase;
2612
+ exports.lerp = lerp;
2363
2613
  exports.memo = memo;
2364
2614
  exports.noop = noop;
2365
2615
  exports.not = not;
@@ -2370,6 +2620,7 @@ exports.passWith = passWith;
2370
2620
  exports.pipe = pipe;
2371
2621
  exports.randomBase32String = randomBase32String;
2372
2622
  exports.randomChoice = randomChoice;
2623
+ exports.randomDistribution = randomDistribution;
2373
2624
  exports.randomHexString = randomHexString;
2374
2625
  exports.randomInt = randomInt;
2375
2626
  exports.randomIntFloor = randomIntFloor;
@@ -2377,11 +2628,13 @@ exports.randomString = randomString;
2377
2628
  exports.range = range;
2378
2629
  exports.remove = remove;
2379
2630
  exports.retry = retry;
2631
+ exports.romanNumerals = romanNumerals;
2380
2632
  exports.round = round;
2381
2633
  exports.roundBank = roundBank;
2382
2634
  exports.roundBase = roundBase;
2383
2635
  exports.roundCeil = roundCeil;
2384
2636
  exports.roundFloor = roundFloor;
2637
+ exports.scientificNotation = scientificNotation;
2385
2638
  exports.shuffle = shuffle;
2386
2639
  exports.sleep = sleep;
2387
2640
  exports.snakeCase = snakeCase;
package/lib/index.d.ts CHANGED
@@ -24,6 +24,56 @@ declare function cartesianProduct<T1, T2, T3, T4, T5, T6, T7>(arr1: ArrayLike<T1
24
24
  declare function cartesianProduct<T1, T2, T3, T4, T5, T6, T7, T8>(arr1: ArrayLike<T1>, arr2: ArrayLike<T2>, arr3: ArrayLike<T3>, arr4: ArrayLike<T4>, arr5: ArrayLike<T5>, arr6: ArrayLike<T6>, arr7: ArrayLike<T7>, arr8: ArrayLike<T8>): [T1, T2, T3, T4, T5, T6, T7, T8][];
25
25
  declare function cartesianProduct(...arrList: ArrayLike<any>[]): any[][];
26
26
 
27
+ /**
28
+ * 将值转换为一个数组
29
+ * @param value 需要处理的值
30
+ * @returns 一个数组, 如果输入值是数组, 则输出的数组是其**浅拷贝**; 如果输入不是数组,则将输入包进一个新数组里
31
+ * @example
32
+ * ```js
33
+ * // 如果输入不是数组,则将输入包进一个新数组里
34
+ * castArray(1) // [1]
35
+ * castArray('1') // ['1']
36
+ * castArray(null) // [null]
37
+ *
38
+ * // 如果输入值是数组, 则输出的数组是其浅拷贝
39
+ * castArray([1, 2, 3, 4, 5]) // [1, 2, 3, 4, 5]
40
+ * ```
41
+ * @version 0.3.3
42
+ */
43
+ declare function castArray<T>(value: T): CastArray<T>;
44
+ /**
45
+ * 将任意输入处理为数组:
46
+ * 1. `never` 类型判断, 确保 `castArray<never>` 返回 `never[]` 而不是 `unknown[]`
47
+ * 2. `unknown` 类型判断, `unknown` 是顶级类型, 需要特殊处理
48
+ * 3. `readonly any[]` 类型判断, 输出的类型需要去除 `readonly`
49
+ * 4. 非数组类型判断, 处理联合类型
50
+ *
51
+ * 数组类型判据:
52
+ * - 如果 `Exclude<T, readonly any[]> extends never` 意味着 `T` 的类型与 `any[]` 兼容, 此时应该走逻辑 3
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * type T = CastArray<string> // string[]
57
+ * type T = CastArray<string[]> // string[]
58
+ * type T = CastArray<readonly string[]> // string[]
59
+ * // 特殊类型
60
+ * type T = CastArray<any> // any[]
61
+ * type T = CastArray<unknown> // unknown[]
62
+ * type T = CastArray<never> // never[]
63
+ * // 联合类型
64
+ * type T = CastArray<string | number> // (string | number)[]
65
+ * type T = CastArray<string[] | number> // string[] | number[]
66
+ * type T = CastArray<string | number | never> // (string | number)[]
67
+ * type T = CastArray<string | number[] | never[]> // never[] | number[] | string[]
68
+ * type T = CastArray<string | number | never[]> // never[] | (string | number)[]
69
+ * ```
70
+ */
71
+ type CastArray<T> = [
72
+ T
73
+ ] extends [never] ? never[] : [
74
+ unknown
75
+ ] extends [T] ? unknown[] : (T extends readonly (infer U)[] ? U[] : never) | (Exclude<T, readonly any[]> extends never ? never : Exclude<T, readonly any[]>[]);
76
+
27
77
  type Not<T> = T extends false | 0 | '' | null | undefined ? true : false;
28
78
 
29
79
  type Stringify<T extends string | number | boolean | bigint | null | undefined> = `${T}`;
@@ -253,24 +303,6 @@ declare function retry<T>(asyncFunction: RetryFunction<T>, option?: RetryOption)
253
303
  declare function sleep(time?: number): Promise<unknown>;
254
304
 
255
305
  type TryitResult<Res, Err> = [undefined, Res] | [Err, undefined];
256
- /**
257
- * 将一个函数处理为 `error-first` 的函数
258
- * @param fn 需要处理的函数, 可以是异步函数
259
- * @returns 处理过的函数, 调用后返回一个 `error-first` 的元组 `[error, result]`;\
260
- * 如果原函数是异步函数, 则返回值会是 `Promise<[error, result]>`;\
261
- * 如果运行正常则 `result` 是原函数的返回值, `error` 为 `undefined`;\
262
- * 如果出现异常则 `result` 为 `undefined`, `error` 是原函数抛出的错误
263
- * @example
264
- * ```js
265
- * // Async Function
266
- * const normalAsyncFn = () => { return Promise.resolve(1) }
267
- * const errorAsyncFn = () => { return Promise.reject('1') }
268
- * tryit(normalAsyncFn)() // Promise<[undefined, 1]>
269
- * tryit(errorAsyncFn)() // Promise<[Error('1'), undefined]>
270
- * ```
271
- * @version 0.2.0
272
- */
273
- declare function tryit<Args extends any[], Res, Err extends Error>(fn: (...args: Args) => Promise<Res>): (...args: Args) => Promise<TryitResult<Res, Err>>;
274
306
  /**
275
307
  * 将一个函数处理为 `error-first` 的函数
276
308
  * @param fn 需要处理的函数, 可以是异步函数
@@ -285,9 +317,15 @@ declare function tryit<Args extends any[], Res, Err extends Error>(fn: (...args:
285
317
  * const errorFn = () => { throw new Error('1') }
286
318
  * tryit(normalFn)() // [undefined, 1]
287
319
  * tryit(errorFn)() // [Error('1'), undefined]
320
+ * // Async Function
321
+ * const normalAsyncFn = () => { return Promise.resolve(1) }
322
+ * const errorAsyncFn = () => { return Promise.reject(new Error('1')) }
323
+ * tryit(normalAsyncFn)() // Promise<[undefined, 1]>
324
+ * tryit(errorAsyncFn)() // Promise<[Error('1'), undefined]>
288
325
  * ```
289
326
  * @version 0.2.0
290
327
  */
328
+ declare function tryit<Args extends any[], Res, Err extends Error>(fn: (...args: Args) => Promise<Res>): (...args: Args) => Promise<TryitResult<Res, Err>>;
291
329
  declare function tryit<Args extends any[], Res, Err extends Error>(fn: (...args: Args) => Res): (...args: Args) => TryitResult<Res, Err>;
292
330
 
293
331
  interface PromiseLikeConstructor {
@@ -963,14 +1001,15 @@ declare function clamp(num: number, min: number, max: number, options?: {
963
1001
  declare function decimalNotation(num: string | number): string;
964
1002
 
965
1003
  /**
966
- * 将一个数字转换为指定的格式(如千分位逗号分隔)
1004
+ * 将一个数字转换为指定的格式(如千分位逗号分隔)\
1005
+ * 如果想要将数字转换为科学计数法, 请使用 `scientificNotation`
967
1006
  * @param num 需要格式化的数字
968
1007
  * @param options 格式化配置\
969
- * - `separator` 分割符, 默认为 `','`
970
- * - `separate` 按位分割, 默认为 `3`
971
- * - `decimal` 小数点, 默认为 `'.'`
972
- * - `precision` 小数精度, 默认为 `2`
973
- * - `round` 数值修约规则
1008
+ * `separator` 分割符, 默认为 `','`\
1009
+ * `separate` 按位分割, 默认为 `3`\
1010
+ * `decimal` 小数点, 默认为 `'.'`\
1011
+ * `precision` 小数精度, 默认为 `2`\
1012
+ * `round` 数值修约规则
974
1013
  * - `'round'` 四舍五入
975
1014
  * - `'banker'` 四舍六入五成双
976
1015
  * - `'floor'` 向下取整
@@ -1001,6 +1040,84 @@ declare function isOdd(num: number): boolean;
1001
1040
  */
1002
1041
  declare function isEven(num: number): boolean;
1003
1042
 
1043
+ /**
1044
+ * 用线性插值法求取一个中间值
1045
+ * @param val1 第一个值, 当 权重=0 时返回的将是这个值, 可以是数组或二维数组
1046
+ * @param val2 第二个值, 当 权重=0 时返回的将是这个值, 可以是数组或二维数组
1047
+ * @param t 权重
1048
+ * @example
1049
+ * ```js
1050
+ * // 传入数值
1051
+ * lerp(1, 2, 0) // 1
1052
+ * lerp(1, 2, 0.5) // 1.5
1053
+ * lerp(1, 2, 1) // 2
1054
+ * lerp(1, 2, 3) // 4
1055
+ *
1056
+ * // 传入数组(数组长度必须一致否则会报错)
1057
+ * lerp([1, 2], [2, 4], 0.5) // [1.5, 3]
1058
+ *
1059
+ * // 传入二维数组(数组长度必须一致否则会报错)
1060
+ * lerp(
1061
+ * [
1062
+ * [1, 2],
1063
+ * [3, 4],
1064
+ * ],
1065
+ * [
1066
+ * [11, 12],
1067
+ * [13, 14],
1068
+ * ],
1069
+ * 0.5
1070
+ * )
1071
+ * // [
1072
+ * // [6, 7],
1073
+ * // [8, 9],
1074
+ * // ]
1075
+ * ```
1076
+ * @version 0.3.3
1077
+ */
1078
+ declare function lerp(val1: number, val2: number, t: number): number;
1079
+ declare function lerp(val1: [number], val2: [number], t: number): [number];
1080
+ declare function lerp(val1: [number, number], val2: [number, number], t: number): [number, number];
1081
+ declare function lerp(val1: [number, number, number], val2: [number, number, number], t: number): [number, number, number];
1082
+ declare function lerp(val1: [number, number, number, number], val2: [number, number, number, number], t: number): [number, number, number, number];
1083
+ declare function lerp(val1: [number, number, number, number, number], val2: [number, number, number, number, number], t: number): [number, number, number, number, number];
1084
+ declare function lerp(val1: [number, number, number, number, number, number], val2: [number, number, number, number, number, number], t: number): [number, number, number, number, number, number];
1085
+ declare function lerp(val1: [number, number, number, number, number, number, number], val2: [number, number, number, number, number, number, number], t: number): [number, number, number, number, number, number, number];
1086
+ declare function lerp(val1: [number, number, number, number, number, number, number, number], val2: [number, number, number, number, number, number, number, number], t: number): [number, number, number, number, number, number, number, number];
1087
+ declare function lerp(val1: number[], val2: number[], t: number): number[];
1088
+ declare function lerp(val1: number[][], val2: number[][], t: number): number[][];
1089
+
1090
+ /**
1091
+ * 将一个数字转换为罗马数字, 罗马数字没有 `0`、小数和负数
1092
+ * @param num 需要转换的数字
1093
+ * @param options 格式化配置\
1094
+ * `type` 类型, 默认为 `unicode`, 以数字 `4090` 为例
1095
+ * - `'unicode'` 使用 Unicode 表示 `'I̅V̅XC'`
1096
+ * - `'js'` 适合用于 JavaScript 代码字符串 `'I\\u0305V\\u0305XC'`
1097
+ * - `'html'` 适合用于 HTML 展示的字符串 `'I&#x0305;V&#x0305;XC'`
1098
+ * - `'json'` 一个 JSON 字符串, 具体数值是下标^1000 `["XC", "IV"]`
1099
+ *
1100
+ * `thousand` 千分位类型, 默认为 `normal`
1101
+ * - `'normal'` 习惯用法, 超过 3999 的部分才使用上划线区分
1102
+ * - `'strict'` 严格区分千分位
1103
+ *
1104
+ * `zero` 用什么字符串表示 `0` (罗马数字里没有 `0`), 不填默认为 `'0'`\
1105
+ * `one` 用什么字符串表示 `1` (你可以设置一个彩蛋, 比如输出 `Yo`), 不填默认为 `'I'`, 这项**只会影响数字 1**
1106
+ * @returns 返回一个罗马数字表示的数字
1107
+ * @example
1108
+ * ```js
1109
+ * romanNumerals(2025) // MMXXV
1110
+ * romanNumerals(2025, { thousand: 'strict' }) // I̅I̅XXV
1111
+ * ```
1112
+ * @version 0.3.3
1113
+ */
1114
+ declare function romanNumerals(num: string | number, options?: {
1115
+ type?: 'unicode' | 'js' | 'html' | 'json';
1116
+ thousand?: 'normal' | 'strict';
1117
+ zero?: string;
1118
+ one?: string;
1119
+ }): string;
1120
+
1004
1121
  /**
1005
1122
  * 数值修约, 可以使用 4 种修约方法, 默认使用四舍五入
1006
1123
  * - 四舍五入: 修约位置的后一位如果是 5 则进位, 否则不进位
@@ -1106,6 +1223,51 @@ declare function roundFloor(integer: string, fractional: string, precision: numb
1106
1223
  */
1107
1224
  declare function roundCeil(integer: string, fractional: string, precision: number, isNegative: boolean): [integer: string, fractional: string];
1108
1225
 
1226
+ /**
1227
+ * 将一个数字转换为科学计数法
1228
+ * @param num 需要转换的数字
1229
+ * @param options 格式化配置\
1230
+ * `type` 类型, 默认为 `unicode`
1231
+ * - `'unicode'` 指数部分使用 Unicode 表示 `1.23×10⁴⁵⁶`
1232
+ * - `'exp'` 用指数表示法, 用 `e` 替换 `×10` 部分 `1.23e+456`
1233
+ * - `'js'` 适合用于 JavaScript 的算式 `1.23*10**456`
1234
+ * - `'code'` 适合用于其他计算机语言的算式 `1.23*10^456`
1235
+ * - `'html'` 适合用于 HTML 展示的字符串 `1.23&#x00d7;10<sup>456</sup>`
1236
+ * - `'json'` 一个 JSON 字符串, 可以自由处理 `{"number":1.23,"exp":456}`
1237
+ *
1238
+ * `precision` 小数精度, 默认不作数值修约\
1239
+ * `round` 数值修约规则
1240
+ * - `'round'` 四舍五入
1241
+ * - `'banker'` 四舍六入五成双
1242
+ * - `'floor'` 向下取整
1243
+ * - `'ceil'` 向上取整
1244
+ * @returns 返回一个科学计数法表示的数字
1245
+ * @example
1246
+ * ```js
1247
+ * scientificNotation(1e12) // 1×10¹²
1248
+ * scientificNotation(-2.33e-8) // -2.33×10⁻⁸
1249
+ * // 可以指定输出类型
1250
+ * scientificNotation(1.234e-6, { type: 'exp' }) // 1.234e-6
1251
+ * scientificNotation(1.234e6, { type: 'exp' }) // 1.234e+6
1252
+ * scientificNotation(6.534e-6, { type: 'code' }) // 6.534*10^-6
1253
+ * scientificNotation(6.534e6, { type: 'code' }) // 6.534*10^6
1254
+ * scientificNotation(-4.321e-8, { type: 'html' }) // -4.321&#x00d7;10<sup>-8</sup>
1255
+ * scientificNotation(-4.321e8, { type: 'html' }) // -4.321&#x00d7;10<sup>8</sup>
1256
+ * scientificNotation(-9.87e-9, { type: 'json' }) // {"number":"-9.87","exp":-9}
1257
+ * scientificNotation(-9.87e9, { type: 'json' }) // {"number":"-9.87","exp":9}
1258
+ * // 可以指定小数点后的位数及数值修约规则
1259
+ * scientificNotation(1.235e6, { type: 'exp', precision: 2 }) // 1.24e+6
1260
+ * scientificNotation(6.545e-6, { type: 'code', precision: 2, round: 'banker' }) // 6.54*10^-6
1261
+ * scientificNotation(-9.87e9, { type: 'json', precision: 1, round: 'floor' }) // {"number":"-9.9","exp":9}
1262
+ * ```
1263
+ * @version 0.3.3
1264
+ */
1265
+ declare function scientificNotation(num: string | number, options?: {
1266
+ type?: 'unicode' | 'exp' | 'js' | 'code' | 'html' | 'json';
1267
+ precision?: number;
1268
+ round?: 'round' | 'banker' | 'floor' | 'ceil';
1269
+ }): string;
1270
+
1109
1271
  /**
1110
1272
  * 根据文件名称判断是否匹配支持的文件类型,需要注意 `.C` 与 `.c` 的区别
1111
1273
  * - `text/x-c++src` 对应 `.C`
@@ -1200,6 +1362,35 @@ declare function getAcceptableMIMEByExt(ext: string): string[];
1200
1362
  */
1201
1363
  declare function randomChoice<T>(arr: ArrayLike<T>, weights?: ArrayLike<number>): T;
1202
1364
 
1365
+ /**
1366
+ * 一个广泛使用的[伪随机数生成算法(PRD)](https://github.com/Moushudyx/pseudo-random-distribution)\
1367
+ * 返回一个方法, 输出为布尔型, 可以生成一个比正常随机序列分布更加均匀的伪随机数列\
1368
+ * 可以极大地避免罕见牌型出现, 缺点是初始概率很低(即第一次命中的概率远远小于输入的 `p`)\
1369
+ * 建议在投入使用之前, 先调用 `ceil(1 / p)` 次作为初始化\
1370
+ * 此外需要注意的是, 这个算法的最终的精度并不是完全等于输入的 `p` 值
1371
+ * @param p 整体概率值, 即概率的数学期望
1372
+ * @param options 相关配置
1373
+ * - `threshold` 精度, 数值越小, 精度越高, 序列越接近目标 `p` 值, 默认为 `5e-6`
1374
+ * @returns 一个方法, 按给定的概率值返回 `true` 和 `false`
1375
+ * @example
1376
+ * ```js
1377
+ * const p0_5 = pipe(randomDistribution(0.5), Number)
1378
+ * p0_5() // 0, 1, 0, 0, 1, 1 ...
1379
+ * ```
1380
+ * @version 0.3.3
1381
+ */
1382
+ declare function randomDistribution(p: number, options?: {
1383
+ threshold: number;
1384
+ }): () => boolean;
1385
+ /**
1386
+ * 内部方法, 用于实现 `randomDistribution` 中的 PRD 算法部分, 根据目标概率, 给出一个初始概率
1387
+ * @param targetP 目标概率
1388
+ * @param threshold 精度, 数值越小, 精度越高
1389
+ * @returns PRD 算法需要的初始概率
1390
+ * @version 0.3.3
1391
+ */
1392
+ declare function getInitP(targetP: number, threshold?: number): number;
1393
+
1203
1394
  /**
1204
1395
  * 生成指定范围内的随机整数
1205
1396
  * @param min 随机数的下界,包含于此范围内
@@ -2029,6 +2220,7 @@ declare function isEmpty(value: unknown): boolean;
2029
2220
  * return fib(n - 1) + fib(n - 2)
2030
2221
  * })
2031
2222
  * ```
2223
+ * @version 0.2.1
2032
2224
  */
2033
2225
  declare function memo<TArgs extends any[], TRes>(fn: (...args: TArgs) => TRes, options?: {
2034
2226
  getKey?: (...args: TArgs) => string | number;
@@ -2216,5 +2408,5 @@ declare function throttle<T extends any[]>(fn: (...args: T) => any, delay: numbe
2216
2408
  reset: () => void;
2217
2409
  };
2218
2410
 
2219
- export { $$Empty, _, acceptableFileName, acceptableFileType, camelCase, capitalize, cartesianProduct, caseCamel, caseConvert, caseKebab, casePascal, caseSnake, chunk, clamp, compose, _curryMore as curry, debounce, decimalNotation, dedent, deepClone, deepMerge, defer, fastClone, format, getAcceptableExtByMIME, getAcceptableMIMEByExt, getGlobalThis, getTag, indent, isArray, isArrayBuffer, isArrayLike, isBigInt, isBigInt64Array, isBigUint64Array, isBlob, isBoolean, isBuffer, isDataView, isDate, isEmpty, isEven, isFile, isFloat32Array, isFloat64Array, isFormData, isFunction, isInt16Array, isInt32Array, isInt8Array, isInteger, isIterable, isMap, isMergeEmptyPlaceholder, isNil, isNull, isNumber, isObject, isOdd, isPlaceholder, isPlainObject, isPrimitive, isPromise, isPromiseLike, isRegExp, isSet, isString, isSymbol, isTypedArray, isUint16Array, isUint32Array, isUint8Array, isUint8ClampedArray, isUndefined, isWeakMap, isWeakSet, isWrapperBigInt, isWrapperBoolean, isWrapperNumber, isWrapperObject, isWrapperString, isWrapperSymbol, kebabCase, memo, noop, not, parallel, pascalCase, pass, passWith, pipe, randomBase32String, randomChoice, randomHexString, randomInt, randomIntFloor, randomString, range, remove, retry, round, roundBank, roundBase, roundCeil, roundFloor, shuffle, sleep, snakeCase, splitWords, throttle, titleCase, tryit, ulid, uncapitalize, uuidNil, uuidV4, withResolvers };
2220
- export type { BaseMargeType, Chunked, CloneOptions, CustomCloner, IsNegative, IsPositive, IsZero, MergeOption, MergeStrategy, MergeStrategyFunction, MergeType, MergeTypeStrategy, Not, RangeOptions, SourceMergeType, Stringify, TargetMergeType, TypedArray };
2411
+ export { $$Empty, _, acceptableFileName, acceptableFileType, camelCase, capitalize, cartesianProduct, caseCamel, caseConvert, caseKebab, casePascal, caseSnake, castArray, chunk, clamp, compose, _curryMore as curry, debounce, decimalNotation, dedent, deepClone, deepMerge, defer, fastClone, format, getAcceptableExtByMIME, getAcceptableMIMEByExt, getGlobalThis, getInitP, getTag, indent, isArray, isArrayBuffer, isArrayLike, isBigInt, isBigInt64Array, isBigUint64Array, isBlob, isBoolean, isBuffer, isDataView, isDate, isEmpty, isEven, isFile, isFloat32Array, isFloat64Array, isFormData, isFunction, isInt16Array, isInt32Array, isInt8Array, isInteger, isIterable, isMap, isMergeEmptyPlaceholder, isNil, isNull, isNumber, isObject, isOdd, isPlaceholder, isPlainObject, isPrimitive, isPromise, isPromiseLike, isRegExp, isSet, isString, isSymbol, isTypedArray, isUint16Array, isUint32Array, isUint8Array, isUint8ClampedArray, isUndefined, isWeakMap, isWeakSet, isWrapperBigInt, isWrapperBoolean, isWrapperNumber, isWrapperObject, isWrapperString, isWrapperSymbol, kebabCase, lerp, memo, noop, not, parallel, pascalCase, pass, passWith, pipe, randomBase32String, randomChoice, randomDistribution, randomHexString, randomInt, randomIntFloor, randomString, range, remove, retry, romanNumerals, round, roundBank, roundBase, roundCeil, roundFloor, scientificNotation, shuffle, sleep, snakeCase, splitWords, throttle, titleCase, tryit, ulid, uncapitalize, uuidNil, uuidV4, withResolvers };
2412
+ export type { BaseMargeType, CastArray, Chunked, CloneOptions, CustomCloner, IsNegative, IsPositive, IsZero, MergeOption, MergeStrategy, MergeStrategyFunction, MergeType, MergeTypeStrategy, Not, RangeOptions, SourceMergeType, Stringify, TargetMergeType, TypedArray };
package/lib/index.mjs CHANGED
@@ -43,6 +43,10 @@ function cartesianProduct(...arrList) {
43
43
  return res;
44
44
  }
45
45
 
46
+ function castArray(value) {
47
+ return Array.isArray(value) ? value.slice() : [value];
48
+ }
49
+
46
50
  function chunk(array, size = 2) {
47
51
  const res = [];
48
52
  if (size < 1 || isNaN(size))
@@ -607,6 +611,201 @@ function format(num, options) {
607
611
  return sign + integer;
608
612
  }
609
613
 
614
+ function lerp(val1, val2, t) {
615
+ if (isNumber(val1)) {
616
+ if (!isNumber(val2))
617
+ throw new Error('Invalid val2 parameter: val2 should be a number');
618
+ return _lerp(val1, val2, t);
619
+ }
620
+ else if (isArray(val1)) {
621
+ if (!isArray(val2))
622
+ throw new Error('Invalid val2 parameter: val2 should be an Array');
623
+ if (val1.length !== val2.length) {
624
+ throw new Error(`Invalid val2 parameter: the length of val2 (${val2.length}) should be consistent with val1 (${val1.length})`);
625
+ }
626
+ if (val1.length === 0)
627
+ return [];
628
+ if (isNumber(val1[0])) {
629
+ if (!isNumber(val2[0]))
630
+ throw new Error('Invalid val2 parameter: val2 should be an Array<number>');
631
+ return val1.map((v1, index) => _lerp(v1, val2[index], t));
632
+ }
633
+ else if (isArray(val1[0])) {
634
+ const res = [];
635
+ for (let i = 0; i < val1.length; i++) {
636
+ const arr1 = val1[i];
637
+ const arr2 = val2[i];
638
+ if (arr1.length !== arr2.length) {
639
+ throw new Error(`Invalid val2 parameter: The length of array at index ${i} in val2 (${arr2.length}) must match the length of the corresponding array in val1 (${arr1.length})`);
640
+ }
641
+ res.push(arr1.map((v1, index) => _lerp(v1, arr2[index], t)));
642
+ }
643
+ return res;
644
+ }
645
+ }
646
+ throw new Error('Invalid val1 parameter: val1 should be a number or Array');
647
+ }
648
+ function _lerp(val1, val2, t) {
649
+ return val1 + (val2 - val1) * t;
650
+ }
651
+
652
+ function romanNumerals(num, options) {
653
+ const str = decimalNotation(num);
654
+ if (/NaN|Inf/.test(str))
655
+ return str;
656
+ const type = (options || {}).type || 'unicode';
657
+ const thousand = (options || {}).thousand || 'normal';
658
+ const forceSplitThousand = thousand === 'strict';
659
+ const integer = str.split('.')[0].replace('-', '');
660
+ if (integer === '0') {
661
+ const zero = (options || {}).zero || '0';
662
+ if (type === 'json')
663
+ return `["${zero}"]`;
664
+ else
665
+ return zero;
666
+ }
667
+ if (integer === '1') {
668
+ const one = (options || {}).one || 'I';
669
+ if (type === 'json')
670
+ return `["${one}"]`;
671
+ else
672
+ return one;
673
+ }
674
+ const chunks = chunk(Array.from(integer).reverse(), 3);
675
+ const romanChunks = [];
676
+ for (let i = 0; i < chunks.length; i++) {
677
+ const val = Number(chunks[i].reverse().join(''));
678
+ if (i === chunks.length - 2 && !forceSplitThousand && Number(chunks[i + 1]) < 4) {
679
+ romanChunks.push(number2roman(val + Number(chunks[i + 1]) * 1000));
680
+ break;
681
+ }
682
+ else {
683
+ romanChunks.push(number2roman(val));
684
+ }
685
+ }
686
+ switch (type) {
687
+ case 'js':
688
+ return romanChunks
689
+ .map((str, index) => str
690
+ .split('')
691
+ .map((s) => s + '\\\\u0305'.repeat(index))
692
+ .join(''))
693
+ .reverse()
694
+ .join('');
695
+ case 'html':
696
+ return romanChunks
697
+ .map((str, index) => str
698
+ .split('')
699
+ .map((s) => s + '&#x0305;'.repeat(index))
700
+ .join(''))
701
+ .reverse()
702
+ .join('');
703
+ case 'json':
704
+ return `[${romanChunks.map((str) => `"${str}"`).join(', ')}]`;
705
+ case 'unicode':
706
+ default:
707
+ return romanChunks
708
+ .map((str, index) => str
709
+ .split('')
710
+ .map((s) => s + '\u0305'.repeat(index))
711
+ .join(''))
712
+ .reverse()
713
+ .join('');
714
+ }
715
+ }
716
+ function number2roman(num) {
717
+ const symbols = [
718
+ [1000, 'M'],
719
+ [900, 'CM'],
720
+ [500, 'D'],
721
+ [400, 'CD'],
722
+ [100, 'C'],
723
+ [90, 'XC'],
724
+ [50, 'L'],
725
+ [40, 'XL'],
726
+ [10, 'X'],
727
+ [9, 'IX'],
728
+ [5, 'V'],
729
+ [4, 'IV'],
730
+ [1, 'I'],
731
+ ];
732
+ let roman = '';
733
+ for (const [val, str] of symbols) {
734
+ while (num >= val) {
735
+ num -= val;
736
+ roman += str;
737
+ }
738
+ if (num == 0)
739
+ break;
740
+ }
741
+ return roman;
742
+ }
743
+
744
+ function scientificNotation(num, options) {
745
+ const str = decimalNotation(num);
746
+ if (/NaN|Inf/.test(str))
747
+ return str;
748
+ const type = (options || {}).type || 'unicode';
749
+ const _precision = (options || {}).precision;
750
+ const precision = isNumber(_precision) ? clamp(_precision !== null && _precision !== void 0 ? _precision : 2, 0, Infinity) : null;
751
+ const round = (options || {}).round || 'round';
752
+ let [integer, fractional] = str.split('.');
753
+ let sign = '';
754
+ if (/^-/.test(integer)) {
755
+ integer = integer.substring(1);
756
+ sign = '-';
757
+ }
758
+ fractional = fractional !== null && fractional !== void 0 ? fractional : '';
759
+ let exp = 0;
760
+ let n = '';
761
+ if (integer === '0') {
762
+ exp = /^(0+)/.test(fractional) ? -(fractional.match(/^(0+)/)[0].length + 1) : -1;
763
+ [integer, fractional] = [fractional.slice(-exp - 1, -exp), fractional.slice(-exp)];
764
+ }
765
+ else {
766
+ exp = integer.length - 1;
767
+ [integer, fractional] = [integer.slice(0, 1), integer.slice(1) + fractional];
768
+ }
769
+ if (isNumber(precision)) {
770
+ if (fractional.length > precision) {
771
+ const roundMap = { round: roundBase, banker: roundBank, floor: roundFloor, ceil: roundCeil };
772
+ [integer, fractional] = (roundMap[round] || roundBase)(integer, fractional, precision, !!sign);
773
+ }
774
+ else if (fractional.length < precision) {
775
+ fractional += '0'.repeat(precision - fractional.length);
776
+ }
777
+ }
778
+ else {
779
+ fractional = fractional.replace(/0+$/, '');
780
+ }
781
+ if ((precision === null && fractional) || (isNumber(precision) && precision > 0)) {
782
+ n = sign + integer + '.' + fractional;
783
+ }
784
+ else
785
+ n = sign + integer;
786
+ switch (type) {
787
+ case 'exp':
788
+ return `${n}e${exp < 0 ? '' : '+'}${exp}`;
789
+ case 'js':
790
+ return `${n}*10**${exp}`;
791
+ case 'code':
792
+ return `${n}*10^${exp}`;
793
+ case 'html':
794
+ return `${n}&#x00d7;10<sup>${exp}</sup>`;
795
+ case 'json':
796
+ return `{"number":"${n}","exp":${exp}}`;
797
+ case 'unicode':
798
+ default:
799
+ return `${n}×10${transferNumberToUniCode(String(exp))}`;
800
+ }
801
+ }
802
+ function transferNumberToUniCode(n) {
803
+ const strMap = Object.assign(Object.assign({}, Array.from('⁰¹²³⁴⁵⁶⁷⁸⁹')), { '-': '⁻', '+': '⁺' });
804
+ return Array.from(n)
805
+ .map((s) => (strMap[s] ? strMap[s] : s))
806
+ .join('');
807
+ }
808
+
610
809
  function parallel(args, fn, options) {
611
810
  return __awaiter(this, void 0, void 0, function* () {
612
811
  if (!args.length)
@@ -1238,6 +1437,54 @@ function randomChoice(arr, weights) {
1238
1437
  return arr[index];
1239
1438
  }
1240
1439
 
1440
+ function randomDistribution(p, options) {
1441
+ const threshold = (options === null || options === void 0 ? void 0 : options.threshold) ? Math.max(options.threshold, Number.MIN_VALUE * 2) : 5e-6;
1442
+ const C = getInitP(p, threshold);
1443
+ let current = C;
1444
+ return function getRandomDistribution() {
1445
+ const res = Math.random() < current;
1446
+ if (res)
1447
+ current = C;
1448
+ else
1449
+ current += C;
1450
+ return res;
1451
+ };
1452
+ }
1453
+ function getInitP(targetP, threshold = 5e-6) {
1454
+ if (targetP <= 0)
1455
+ return 0;
1456
+ if (targetP >= 1)
1457
+ return 1;
1458
+ let [down, up] = [0, 1];
1459
+ let mid = 1;
1460
+ let tempP = 1;
1461
+ let tempPLast = 1;
1462
+ let step = 64;
1463
+ while (step-- > 0) {
1464
+ mid = (down + up) / 2;
1465
+ tempP = getRealPFromInitP(mid);
1466
+ if (Math.abs(tempPLast - tempP) < threshold)
1467
+ break;
1468
+ if (tempP > targetP)
1469
+ up = mid;
1470
+ else
1471
+ down = mid;
1472
+ }
1473
+ return mid;
1474
+ }
1475
+ function getRealPFromInitP(initP) {
1476
+ let sum = 0;
1477
+ let prod = 0;
1478
+ let cur = 0;
1479
+ let max = Math.ceil(1 / initP);
1480
+ for (let n = 1; n <= max; n++) {
1481
+ cur = Math.min(1, n * initP) * (1 - prod);
1482
+ prod += cur;
1483
+ sum += n * cur;
1484
+ }
1485
+ return 1 / sum;
1486
+ }
1487
+
1241
1488
  const radix32 = '0123456789abcdefghijklmnopqrstuv';
1242
1489
  const base32Chars = 'abcdefghijklmnopqrstuvwxyz234567';
1243
1490
  const base32Crockford = '0123456789abcdefghjkmnpqrstvwxyz';
@@ -1864,22 +2111,22 @@ function _deepClone(obj, map, options) {
1864
2111
  if (isFormData(obj))
1865
2112
  return _cloneFormData(obj, map, _deepClone, options);
1866
2113
  let res;
1867
- if (obj instanceof Date) {
2114
+ if (isDate(obj)) {
1868
2115
  res = new Date(obj.valueOf());
1869
2116
  map.set(obj, res);
1870
2117
  }
1871
- else if (obj instanceof RegExp) {
2118
+ else if (isRegExp(obj)) {
1872
2119
  res = new RegExp(obj.source, obj.flags);
1873
2120
  map.set(obj, res);
1874
2121
  }
1875
- else if (obj instanceof ArrayBuffer) {
2122
+ else if (isArrayBuffer(obj)) {
1876
2123
  res = _cloneArrayBuffer(obj, map);
1877
2124
  }
1878
2125
  else if (isTypedArray(obj)) {
1879
2126
  res = new obj.constructor(_cloneArrayBuffer(obj.buffer, map), obj.byteOffset, obj.length);
1880
2127
  map.set(obj, res);
1881
2128
  }
1882
- else if (obj instanceof DataView) {
2129
+ else if (isDataView(obj)) {
1883
2130
  res = new DataView(map.has(obj.buffer) ? map.get(obj.buffer) : _cloneArrayBuffer(obj.buffer, map), obj.byteOffset, obj.byteLength);
1884
2131
  map.set(obj, res);
1885
2132
  }
@@ -2275,4 +2522,4 @@ function throttle(fn, delay, options) {
2275
2522
  return _throttle(fn, delay, Object.assign({ trailing: false, leading: true }, options));
2276
2523
  }
2277
2524
 
2278
- export { $$Empty, _, acceptableFileName, acceptableFileType, camelCase, capitalize, cartesianProduct, caseCamel, caseConvert, caseKebab, casePascal, caseSnake, chunk, clamp, compose, _curryMore as curry, debounce, decimalNotation, dedent, deepClone, deepMerge, defer, fastClone, format, getAcceptableExtByMIME, getAcceptableMIMEByExt, getGlobalThis, getTag, indent, isArray, isArrayBuffer, isArrayLike, isBigInt, isBigInt64Array, isBigUint64Array, isBlob, isBoolean, isBuffer, isDataView, isDate, isEmpty, isEven, isFile, isFloat32Array, isFloat64Array, isFormData, isFunction, isInt16Array, isInt32Array, isInt8Array, isInteger, isIterable, isMap, isMergeEmptyPlaceholder, isNil, isNull, isNumber, isObject, isOdd, isPlaceholder, isPlainObject, isPrimitive, isPromise, isPromiseLike, isRegExp, isSet, isString, isSymbol, isTypedArray, isUint16Array, isUint32Array, isUint8Array, isUint8ClampedArray, isUndefined, isWeakMap, isWeakSet, isWrapperBigInt, isWrapperBoolean, isWrapperNumber, isWrapperObject, isWrapperString, isWrapperSymbol, kebabCase, memo, noop, not, parallel, pascalCase, pass, passWith, pipe, randomBase32String, randomChoice, randomHexString, randomInt, randomIntFloor, randomString, range, remove, retry, round, roundBank, roundBase, roundCeil, roundFloor, shuffle, sleep, snakeCase, splitWords, throttle, titleCase, tryit, ulid, uncapitalize, uuidNil, uuidV4, withResolvers };
2525
+ export { $$Empty, _, acceptableFileName, acceptableFileType, camelCase, capitalize, cartesianProduct, caseCamel, caseConvert, caseKebab, casePascal, caseSnake, castArray, chunk, clamp, compose, _curryMore as curry, debounce, decimalNotation, dedent, deepClone, deepMerge, defer, fastClone, format, getAcceptableExtByMIME, getAcceptableMIMEByExt, getGlobalThis, getInitP, getTag, indent, isArray, isArrayBuffer, isArrayLike, isBigInt, isBigInt64Array, isBigUint64Array, isBlob, isBoolean, isBuffer, isDataView, isDate, isEmpty, isEven, isFile, isFloat32Array, isFloat64Array, isFormData, isFunction, isInt16Array, isInt32Array, isInt8Array, isInteger, isIterable, isMap, isMergeEmptyPlaceholder, isNil, isNull, isNumber, isObject, isOdd, isPlaceholder, isPlainObject, isPrimitive, isPromise, isPromiseLike, isRegExp, isSet, isString, isSymbol, isTypedArray, isUint16Array, isUint32Array, isUint8Array, isUint8ClampedArray, isUndefined, isWeakMap, isWeakSet, isWrapperBigInt, isWrapperBoolean, isWrapperNumber, isWrapperObject, isWrapperString, isWrapperSymbol, kebabCase, lerp, memo, noop, not, parallel, pascalCase, pass, passWith, pipe, randomBase32String, randomChoice, randomDistribution, randomHexString, randomInt, randomIntFloor, randomString, range, remove, retry, romanNumerals, round, roundBank, roundBase, roundCeil, roundFloor, scientificNotation, shuffle, sleep, snakeCase, splitWords, throttle, titleCase, tryit, ulid, uncapitalize, uuidNil, uuidV4, withResolvers };
package/lib/index.umd.js CHANGED
@@ -49,6 +49,10 @@ See the Mulan PSL v2 for more details.
49
49
  return res;
50
50
  }
51
51
 
52
+ function castArray(value) {
53
+ return Array.isArray(value) ? value.slice() : [value];
54
+ }
55
+
52
56
  function chunk(array, size = 2) {
53
57
  const res = [];
54
58
  if (size < 1 || isNaN(size))
@@ -613,6 +617,201 @@ See the Mulan PSL v2 for more details.
613
617
  return sign + integer;
614
618
  }
615
619
 
620
+ function lerp(val1, val2, t) {
621
+ if (isNumber(val1)) {
622
+ if (!isNumber(val2))
623
+ throw new Error('Invalid val2 parameter: val2 should be a number');
624
+ return _lerp(val1, val2, t);
625
+ }
626
+ else if (isArray(val1)) {
627
+ if (!isArray(val2))
628
+ throw new Error('Invalid val2 parameter: val2 should be an Array');
629
+ if (val1.length !== val2.length) {
630
+ throw new Error(`Invalid val2 parameter: the length of val2 (${val2.length}) should be consistent with val1 (${val1.length})`);
631
+ }
632
+ if (val1.length === 0)
633
+ return [];
634
+ if (isNumber(val1[0])) {
635
+ if (!isNumber(val2[0]))
636
+ throw new Error('Invalid val2 parameter: val2 should be an Array<number>');
637
+ return val1.map((v1, index) => _lerp(v1, val2[index], t));
638
+ }
639
+ else if (isArray(val1[0])) {
640
+ const res = [];
641
+ for (let i = 0; i < val1.length; i++) {
642
+ const arr1 = val1[i];
643
+ const arr2 = val2[i];
644
+ if (arr1.length !== arr2.length) {
645
+ throw new Error(`Invalid val2 parameter: The length of array at index ${i} in val2 (${arr2.length}) must match the length of the corresponding array in val1 (${arr1.length})`);
646
+ }
647
+ res.push(arr1.map((v1, index) => _lerp(v1, arr2[index], t)));
648
+ }
649
+ return res;
650
+ }
651
+ }
652
+ throw new Error('Invalid val1 parameter: val1 should be a number or Array');
653
+ }
654
+ function _lerp(val1, val2, t) {
655
+ return val1 + (val2 - val1) * t;
656
+ }
657
+
658
+ function romanNumerals(num, options) {
659
+ const str = decimalNotation(num);
660
+ if (/NaN|Inf/.test(str))
661
+ return str;
662
+ const type = (options || {}).type || 'unicode';
663
+ const thousand = (options || {}).thousand || 'normal';
664
+ const forceSplitThousand = thousand === 'strict';
665
+ const integer = str.split('.')[0].replace('-', '');
666
+ if (integer === '0') {
667
+ const zero = (options || {}).zero || '0';
668
+ if (type === 'json')
669
+ return `["${zero}"]`;
670
+ else
671
+ return zero;
672
+ }
673
+ if (integer === '1') {
674
+ const one = (options || {}).one || 'I';
675
+ if (type === 'json')
676
+ return `["${one}"]`;
677
+ else
678
+ return one;
679
+ }
680
+ const chunks = chunk(Array.from(integer).reverse(), 3);
681
+ const romanChunks = [];
682
+ for (let i = 0; i < chunks.length; i++) {
683
+ const val = Number(chunks[i].reverse().join(''));
684
+ if (i === chunks.length - 2 && !forceSplitThousand && Number(chunks[i + 1]) < 4) {
685
+ romanChunks.push(number2roman(val + Number(chunks[i + 1]) * 1000));
686
+ break;
687
+ }
688
+ else {
689
+ romanChunks.push(number2roman(val));
690
+ }
691
+ }
692
+ switch (type) {
693
+ case 'js':
694
+ return romanChunks
695
+ .map((str, index) => str
696
+ .split('')
697
+ .map((s) => s + '\\\\u0305'.repeat(index))
698
+ .join(''))
699
+ .reverse()
700
+ .join('');
701
+ case 'html':
702
+ return romanChunks
703
+ .map((str, index) => str
704
+ .split('')
705
+ .map((s) => s + '&#x0305;'.repeat(index))
706
+ .join(''))
707
+ .reverse()
708
+ .join('');
709
+ case 'json':
710
+ return `[${romanChunks.map((str) => `"${str}"`).join(', ')}]`;
711
+ case 'unicode':
712
+ default:
713
+ return romanChunks
714
+ .map((str, index) => str
715
+ .split('')
716
+ .map((s) => s + '\u0305'.repeat(index))
717
+ .join(''))
718
+ .reverse()
719
+ .join('');
720
+ }
721
+ }
722
+ function number2roman(num) {
723
+ const symbols = [
724
+ [1000, 'M'],
725
+ [900, 'CM'],
726
+ [500, 'D'],
727
+ [400, 'CD'],
728
+ [100, 'C'],
729
+ [90, 'XC'],
730
+ [50, 'L'],
731
+ [40, 'XL'],
732
+ [10, 'X'],
733
+ [9, 'IX'],
734
+ [5, 'V'],
735
+ [4, 'IV'],
736
+ [1, 'I'],
737
+ ];
738
+ let roman = '';
739
+ for (const [val, str] of symbols) {
740
+ while (num >= val) {
741
+ num -= val;
742
+ roman += str;
743
+ }
744
+ if (num == 0)
745
+ break;
746
+ }
747
+ return roman;
748
+ }
749
+
750
+ function scientificNotation(num, options) {
751
+ const str = decimalNotation(num);
752
+ if (/NaN|Inf/.test(str))
753
+ return str;
754
+ const type = (options || {}).type || 'unicode';
755
+ const _precision = (options || {}).precision;
756
+ const precision = isNumber(_precision) ? clamp(_precision !== null && _precision !== void 0 ? _precision : 2, 0, Infinity) : null;
757
+ const round = (options || {}).round || 'round';
758
+ let [integer, fractional] = str.split('.');
759
+ let sign = '';
760
+ if (/^-/.test(integer)) {
761
+ integer = integer.substring(1);
762
+ sign = '-';
763
+ }
764
+ fractional = fractional !== null && fractional !== void 0 ? fractional : '';
765
+ let exp = 0;
766
+ let n = '';
767
+ if (integer === '0') {
768
+ exp = /^(0+)/.test(fractional) ? -(fractional.match(/^(0+)/)[0].length + 1) : -1;
769
+ [integer, fractional] = [fractional.slice(-exp - 1, -exp), fractional.slice(-exp)];
770
+ }
771
+ else {
772
+ exp = integer.length - 1;
773
+ [integer, fractional] = [integer.slice(0, 1), integer.slice(1) + fractional];
774
+ }
775
+ if (isNumber(precision)) {
776
+ if (fractional.length > precision) {
777
+ const roundMap = { round: roundBase, banker: roundBank, floor: roundFloor, ceil: roundCeil };
778
+ [integer, fractional] = (roundMap[round] || roundBase)(integer, fractional, precision, !!sign);
779
+ }
780
+ else if (fractional.length < precision) {
781
+ fractional += '0'.repeat(precision - fractional.length);
782
+ }
783
+ }
784
+ else {
785
+ fractional = fractional.replace(/0+$/, '');
786
+ }
787
+ if ((precision === null && fractional) || (isNumber(precision) && precision > 0)) {
788
+ n = sign + integer + '.' + fractional;
789
+ }
790
+ else
791
+ n = sign + integer;
792
+ switch (type) {
793
+ case 'exp':
794
+ return `${n}e${exp < 0 ? '' : '+'}${exp}`;
795
+ case 'js':
796
+ return `${n}*10**${exp}`;
797
+ case 'code':
798
+ return `${n}*10^${exp}`;
799
+ case 'html':
800
+ return `${n}&#x00d7;10<sup>${exp}</sup>`;
801
+ case 'json':
802
+ return `{"number":"${n}","exp":${exp}}`;
803
+ case 'unicode':
804
+ default:
805
+ return `${n}×10${transferNumberToUniCode(String(exp))}`;
806
+ }
807
+ }
808
+ function transferNumberToUniCode(n) {
809
+ const strMap = Object.assign(Object.assign({}, Array.from('⁰¹²³⁴⁵⁶⁷⁸⁹')), { '-': '⁻', '+': '⁺' });
810
+ return Array.from(n)
811
+ .map((s) => (strMap[s] ? strMap[s] : s))
812
+ .join('');
813
+ }
814
+
616
815
  function parallel(args, fn, options) {
617
816
  return __awaiter(this, void 0, void 0, function* () {
618
817
  if (!args.length)
@@ -1244,6 +1443,54 @@ See the Mulan PSL v2 for more details.
1244
1443
  return arr[index];
1245
1444
  }
1246
1445
 
1446
+ function randomDistribution(p, options) {
1447
+ const threshold = (options === null || options === void 0 ? void 0 : options.threshold) ? Math.max(options.threshold, Number.MIN_VALUE * 2) : 5e-6;
1448
+ const C = getInitP(p, threshold);
1449
+ let current = C;
1450
+ return function getRandomDistribution() {
1451
+ const res = Math.random() < current;
1452
+ if (res)
1453
+ current = C;
1454
+ else
1455
+ current += C;
1456
+ return res;
1457
+ };
1458
+ }
1459
+ function getInitP(targetP, threshold = 5e-6) {
1460
+ if (targetP <= 0)
1461
+ return 0;
1462
+ if (targetP >= 1)
1463
+ return 1;
1464
+ let [down, up] = [0, 1];
1465
+ let mid = 1;
1466
+ let tempP = 1;
1467
+ let tempPLast = 1;
1468
+ let step = 64;
1469
+ while (step-- > 0) {
1470
+ mid = (down + up) / 2;
1471
+ tempP = getRealPFromInitP(mid);
1472
+ if (Math.abs(tempPLast - tempP) < threshold)
1473
+ break;
1474
+ if (tempP > targetP)
1475
+ up = mid;
1476
+ else
1477
+ down = mid;
1478
+ }
1479
+ return mid;
1480
+ }
1481
+ function getRealPFromInitP(initP) {
1482
+ let sum = 0;
1483
+ let prod = 0;
1484
+ let cur = 0;
1485
+ let max = Math.ceil(1 / initP);
1486
+ for (let n = 1; n <= max; n++) {
1487
+ cur = Math.min(1, n * initP) * (1 - prod);
1488
+ prod += cur;
1489
+ sum += n * cur;
1490
+ }
1491
+ return 1 / sum;
1492
+ }
1493
+
1247
1494
  const radix32 = '0123456789abcdefghijklmnopqrstuv';
1248
1495
  const base32Chars = 'abcdefghijklmnopqrstuvwxyz234567';
1249
1496
  const base32Crockford = '0123456789abcdefghjkmnpqrstvwxyz';
@@ -1870,22 +2117,22 @@ See the Mulan PSL v2 for more details.
1870
2117
  if (isFormData(obj))
1871
2118
  return _cloneFormData(obj, map, _deepClone, options);
1872
2119
  let res;
1873
- if (obj instanceof Date) {
2120
+ if (isDate(obj)) {
1874
2121
  res = new Date(obj.valueOf());
1875
2122
  map.set(obj, res);
1876
2123
  }
1877
- else if (obj instanceof RegExp) {
2124
+ else if (isRegExp(obj)) {
1878
2125
  res = new RegExp(obj.source, obj.flags);
1879
2126
  map.set(obj, res);
1880
2127
  }
1881
- else if (obj instanceof ArrayBuffer) {
2128
+ else if (isArrayBuffer(obj)) {
1882
2129
  res = _cloneArrayBuffer(obj, map);
1883
2130
  }
1884
2131
  else if (isTypedArray(obj)) {
1885
2132
  res = new obj.constructor(_cloneArrayBuffer(obj.buffer, map), obj.byteOffset, obj.length);
1886
2133
  map.set(obj, res);
1887
2134
  }
1888
- else if (obj instanceof DataView) {
2135
+ else if (isDataView(obj)) {
1889
2136
  res = new DataView(map.has(obj.buffer) ? map.get(obj.buffer) : _cloneArrayBuffer(obj.buffer, map), obj.byteOffset, obj.byteLength);
1890
2137
  map.set(obj, res);
1891
2138
  }
@@ -2293,6 +2540,7 @@ See the Mulan PSL v2 for more details.
2293
2540
  exports.caseKebab = caseKebab;
2294
2541
  exports.casePascal = casePascal;
2295
2542
  exports.caseSnake = caseSnake;
2543
+ exports.castArray = castArray;
2296
2544
  exports.chunk = chunk;
2297
2545
  exports.clamp = clamp;
2298
2546
  exports.compose = compose;
@@ -2308,6 +2556,7 @@ See the Mulan PSL v2 for more details.
2308
2556
  exports.getAcceptableExtByMIME = getAcceptableExtByMIME;
2309
2557
  exports.getAcceptableMIMEByExt = getAcceptableMIMEByExt;
2310
2558
  exports.getGlobalThis = getGlobalThis;
2559
+ exports.getInitP = getInitP;
2311
2560
  exports.getTag = getTag;
2312
2561
  exports.indent = indent;
2313
2562
  exports.isArray = isArray;
@@ -2364,6 +2613,7 @@ See the Mulan PSL v2 for more details.
2364
2613
  exports.isWrapperString = isWrapperString;
2365
2614
  exports.isWrapperSymbol = isWrapperSymbol;
2366
2615
  exports.kebabCase = kebabCase;
2616
+ exports.lerp = lerp;
2367
2617
  exports.memo = memo;
2368
2618
  exports.noop = noop;
2369
2619
  exports.not = not;
@@ -2374,6 +2624,7 @@ See the Mulan PSL v2 for more details.
2374
2624
  exports.pipe = pipe;
2375
2625
  exports.randomBase32String = randomBase32String;
2376
2626
  exports.randomChoice = randomChoice;
2627
+ exports.randomDistribution = randomDistribution;
2377
2628
  exports.randomHexString = randomHexString;
2378
2629
  exports.randomInt = randomInt;
2379
2630
  exports.randomIntFloor = randomIntFloor;
@@ -2381,11 +2632,13 @@ See the Mulan PSL v2 for more details.
2381
2632
  exports.range = range;
2382
2633
  exports.remove = remove;
2383
2634
  exports.retry = retry;
2635
+ exports.romanNumerals = romanNumerals;
2384
2636
  exports.round = round;
2385
2637
  exports.roundBank = roundBank;
2386
2638
  exports.roundBase = roundBase;
2387
2639
  exports.roundCeil = roundCeil;
2388
2640
  exports.roundFloor = roundFloor;
2641
+ exports.scientificNotation = scientificNotation;
2389
2642
  exports.shuffle = shuffle;
2390
2643
  exports.sleep = sleep;
2391
2644
  exports.snakeCase = snakeCase;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foreslash",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "Foreslash is a Javascript utilities lib which contains plenty of practical functions.",
5
5
  "author": "moushu",
6
6
  "license": "Mulan PSL v2",