@zelgadis87/utils-core 5.3.4 → 5.3.6
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/.rollup/index.cjs +116 -28
- package/.rollup/index.cjs.map +1 -1
- package/.rollup/index.d.ts +29 -7
- package/.rollup/index.mjs +115 -29
- package/.rollup/index.mjs.map +1 -1
- package/.rollup/tsconfig.tsbuildinfo +1 -1
- package/CHANGELOG.md +14 -0
- package/package.json +7 -7
- package/src/time/TimeInstant.ts +111 -50
- package/src/utils/arrays/statistics.ts +16 -7
- package/src/utils/arrays.ts +30 -2
package/.rollup/index.cjs
CHANGED
|
@@ -447,9 +447,14 @@ function indexByWith(arr, keyGetter, valueMapper = v => v) {
|
|
|
447
447
|
}
|
|
448
448
|
|
|
449
449
|
function average(arr) {
|
|
450
|
+
if (arr.length === 0)
|
|
451
|
+
return null;
|
|
450
452
|
const f = 1 / arr.length;
|
|
451
453
|
return arr.reduce((tot, cur) => tot + (cur * f), 0);
|
|
452
454
|
}
|
|
455
|
+
function averageBy(arr, getter) {
|
|
456
|
+
return average(arr.map(getter));
|
|
457
|
+
}
|
|
453
458
|
function sum(arr) {
|
|
454
459
|
return arr.reduce((tot, cur) => tot + cur, 0);
|
|
455
460
|
}
|
|
@@ -458,7 +463,7 @@ function sumBy(arr, getter) {
|
|
|
458
463
|
}
|
|
459
464
|
function min(arr) {
|
|
460
465
|
if (arr.length === 0)
|
|
461
|
-
|
|
466
|
+
return null;
|
|
462
467
|
return arr.reduce((min, cur) => cur < min ? cur : min);
|
|
463
468
|
}
|
|
464
469
|
function minBy(arr, getter) {
|
|
@@ -466,7 +471,7 @@ function minBy(arr, getter) {
|
|
|
466
471
|
}
|
|
467
472
|
function max(arr) {
|
|
468
473
|
if (arr.length === 0)
|
|
469
|
-
|
|
474
|
+
return null;
|
|
470
475
|
return arr.reduce((max, cur) => cur > max ? cur : max);
|
|
471
476
|
}
|
|
472
477
|
function maxBy(arr, getter) {
|
|
@@ -621,9 +626,36 @@ function range(start, end) {
|
|
|
621
626
|
let length = (end - start) + 1;
|
|
622
627
|
return new Array(length).fill(1).map((_, i) => start + i);
|
|
623
628
|
}
|
|
629
|
+
/**
|
|
630
|
+
* Creates an array of the specified length, where each element is filled with the given value.
|
|
631
|
+
* @param length - The length of the array to create. Must be a non-negative integer.
|
|
632
|
+
* @param value - The value to fill each element with.
|
|
633
|
+
* @returns A new array with all elements set to the given value.
|
|
634
|
+
* @throws {RangeError} If length is negative, not an integer, or NaN.
|
|
635
|
+
* @example
|
|
636
|
+
* ```ts
|
|
637
|
+
* fill(3, 'a'); // ['a', 'a', 'a']
|
|
638
|
+
* fill(0, 42); // []
|
|
639
|
+
* fill(5, null); // [null, null, null, null, null]
|
|
640
|
+
* ```
|
|
641
|
+
*/
|
|
624
642
|
function fill(length, value) {
|
|
643
|
+
if (!Number.isInteger(length) || length < 0)
|
|
644
|
+
throw new RangeError(`Length must be a non-negative integer. Got: ${length}`);
|
|
625
645
|
return new Array(length).fill(value);
|
|
626
646
|
}
|
|
647
|
+
/**
|
|
648
|
+
* Creates an array of the specified length, where each element is generated by the provided generator function.
|
|
649
|
+
* @param length - The length of the array to create. Must be a non-negative integer.
|
|
650
|
+
* @param generator - A function that takes an index and returns the value for that position.
|
|
651
|
+
* @returns A new array with elements generated by the generator function.
|
|
652
|
+
* @throws {RangeError} If length is negative or not an integer.
|
|
653
|
+
*/
|
|
654
|
+
function fillWith(length, generator) {
|
|
655
|
+
if (!Number.isInteger(length) || length < 0)
|
|
656
|
+
throw new RangeError(`Length must be a non-negative integer. Got: ${length}`);
|
|
657
|
+
return Array.from({ length }, (_, i) => generator(i));
|
|
658
|
+
}
|
|
627
659
|
function extendArray(arr, props) {
|
|
628
660
|
return arr.map((t) => ({
|
|
629
661
|
...t,
|
|
@@ -2313,8 +2345,48 @@ function normalizeMonthName(name) {
|
|
|
2313
2345
|
// Trim whitespace
|
|
2314
2346
|
.trim();
|
|
2315
2347
|
}
|
|
2316
|
-
const PATTERN_REGEX = /(M|y|d|D|h|H|m|s|S|G|Z|P|a)+/g;
|
|
2317
2348
|
const ESCAPE_REGEX = /\\"|"((?:\\"|[^"])*)"/g;
|
|
2349
|
+
/**
|
|
2350
|
+
* Tokenize a date/time pattern by detecting character changes.
|
|
2351
|
+
*
|
|
2352
|
+
* This function uses a character-change detection algorithm to split a pattern into tokens:
|
|
2353
|
+
* 1. Iterates through the pattern character by character
|
|
2354
|
+
* 2. Detects when the character changes (e.g., 'y' → 'M' → 'd')
|
|
2355
|
+
* 3. Creates tokens based on consecutive identical characters
|
|
2356
|
+
* 4. Marks tokens as pattern tokens only if they consist of repeated pattern characters
|
|
2357
|
+
*
|
|
2358
|
+
* This approach works correctly for patterns with or without separators:
|
|
2359
|
+
* - "yyyy-MM-dd" → [{token:"yyyy", type:"y"}, {token:"-", type:null}, {token:"MM", type:"M"}, {token:"-", type:null}, {token:"dd", type:"d"}]
|
|
2360
|
+
* - "yyyyMMdd" → [{token:"yyyy", type:"y"}, {token:"MM", type:"M"}, {token:"dd", type:"d"}]
|
|
2361
|
+
*
|
|
2362
|
+
* @param pattern - The date/time pattern to tokenize
|
|
2363
|
+
* @returns Array of tokens with their types (null for separators/literals, pattern char for pattern tokens)
|
|
2364
|
+
*/
|
|
2365
|
+
function tokenizePattern(pattern) {
|
|
2366
|
+
const result = [];
|
|
2367
|
+
let currentToken = '';
|
|
2368
|
+
let currentChar = '';
|
|
2369
|
+
for (const char of pattern) {
|
|
2370
|
+
if (char === currentChar) {
|
|
2371
|
+
// Same character, extend current token
|
|
2372
|
+
currentToken += char;
|
|
2373
|
+
}
|
|
2374
|
+
else {
|
|
2375
|
+
// Character changed, end current token and start new one
|
|
2376
|
+
if (currentToken) {
|
|
2377
|
+
// Mark the token with its character (caller will decide if it's a valid pattern)
|
|
2378
|
+
result.push({ token: currentToken, type: currentChar });
|
|
2379
|
+
}
|
|
2380
|
+
currentToken = char;
|
|
2381
|
+
currentChar = char;
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
// Don't forget the last token
|
|
2385
|
+
if (currentToken) {
|
|
2386
|
+
result.push({ token: currentToken, type: currentChar });
|
|
2387
|
+
}
|
|
2388
|
+
return result;
|
|
2389
|
+
}
|
|
2318
2390
|
// Formatter configuration mapping: token pattern -> configuration with optional extraction rules
|
|
2319
2391
|
const formatterConfigs = {
|
|
2320
2392
|
// Year
|
|
@@ -2396,11 +2468,16 @@ function formatTimeInstant(instant, pattern, config = {}) {
|
|
|
2396
2468
|
if (index % 2 !== 0) {
|
|
2397
2469
|
return sub;
|
|
2398
2470
|
}
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2471
|
+
// Tokenize the pattern by detecting character changes
|
|
2472
|
+
const tokens = tokenizePattern(sub);
|
|
2473
|
+
// Format each token and join the results
|
|
2474
|
+
return tokens.map(({ token, type }) => {
|
|
2475
|
+
// If type is null or formatting fails, keep token as-is
|
|
2476
|
+
if (!type)
|
|
2477
|
+
return token;
|
|
2478
|
+
const formatted = formatType(type, token.length, date, locale, timeZone);
|
|
2479
|
+
return formatted !== undefined ? formatted : token;
|
|
2480
|
+
}).join('');
|
|
2404
2481
|
})
|
|
2405
2482
|
.join('');
|
|
2406
2483
|
}
|
|
@@ -2428,53 +2505,62 @@ function parseTimeInstantComponents(dateString, pattern, config = {}) {
|
|
|
2428
2505
|
if (index % 2 !== 0) {
|
|
2429
2506
|
return sub.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // Escape special regex chars
|
|
2430
2507
|
}
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2508
|
+
// Tokenize the pattern by detecting character changes
|
|
2509
|
+
const patternTokens = tokenizePattern(sub);
|
|
2510
|
+
// Build regex pattern from tokens
|
|
2511
|
+
return patternTokens.map(({ token, type }) => {
|
|
2512
|
+
// Check if this is a valid pattern by looking it up in formatterConfigs
|
|
2513
|
+
const isValidPattern = type && formatterConfigs[token];
|
|
2514
|
+
if (!isValidPattern) {
|
|
2515
|
+
// This is a literal/separator, escape it for regex
|
|
2516
|
+
return token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
2517
|
+
}
|
|
2518
|
+
// This is a pattern token, track it and create regex
|
|
2519
|
+
tokens.push({ type, length: token.length, position: position++ });
|
|
2434
2520
|
// Create appropriate regex for each token type
|
|
2435
2521
|
switch (type) {
|
|
2436
2522
|
case 'y':
|
|
2437
|
-
return
|
|
2523
|
+
return token.length === 2 ? '(\\d{2})' : '(\\d{4})';
|
|
2438
2524
|
case 'M':
|
|
2439
|
-
if (
|
|
2525
|
+
if (token.length === 1)
|
|
2440
2526
|
return '(\\d{1,2})';
|
|
2441
|
-
if (
|
|
2527
|
+
if (token.length === 2)
|
|
2442
2528
|
return '(\\d{2})';
|
|
2443
|
-
if (
|
|
2529
|
+
if (token.length === 3)
|
|
2444
2530
|
return '([A-Za-z.]{1,7})';
|
|
2445
2531
|
return '([A-Za-z]+)';
|
|
2446
2532
|
case 'd':
|
|
2447
|
-
return
|
|
2533
|
+
return token.length === 1 ? '(\\d{1,2})' : '(\\d{2})';
|
|
2448
2534
|
case 'H':
|
|
2449
2535
|
case 'h':
|
|
2450
|
-
return
|
|
2536
|
+
return token.length === 1 ? '(\\d{1,2})' : '(\\d{2})';
|
|
2451
2537
|
case 'm':
|
|
2452
2538
|
case 's':
|
|
2453
|
-
return
|
|
2539
|
+
return token.length === 1 ? '(\\d{1,2})' : '(\\d{2})';
|
|
2454
2540
|
case 'S':
|
|
2455
|
-
return `(\\d{${
|
|
2541
|
+
return `(\\d{${token.length}})`;
|
|
2456
2542
|
case 'a':
|
|
2457
2543
|
return '([aApP][mM])';
|
|
2458
2544
|
case 'D':
|
|
2459
|
-
if (
|
|
2545
|
+
if (token.length === 1)
|
|
2460
2546
|
return '([A-Za-z])';
|
|
2461
|
-
if (
|
|
2547
|
+
if (token.length === 2)
|
|
2462
2548
|
return '([A-Za-z]{3})';
|
|
2463
2549
|
return '([A-Za-z]+)';
|
|
2464
2550
|
case 'G':
|
|
2465
|
-
if (
|
|
2551
|
+
if (token.length === 1)
|
|
2466
2552
|
return '([A-Za-z])';
|
|
2467
|
-
if (
|
|
2553
|
+
if (token.length === 2)
|
|
2468
2554
|
return '([A-Za-z]{2})';
|
|
2469
2555
|
return '([A-Za-z\\s]+)';
|
|
2470
2556
|
case 'Z':
|
|
2471
|
-
return
|
|
2557
|
+
return token.length === 1 ? '([A-Za-z0-9+\\-:]+)' : '([A-Za-z\\s]+)';
|
|
2472
2558
|
case 'P':
|
|
2473
2559
|
return '([A-Za-z\\s]+)';
|
|
2474
2560
|
default:
|
|
2475
|
-
return
|
|
2561
|
+
return token;
|
|
2476
2562
|
}
|
|
2477
|
-
});
|
|
2563
|
+
}).join('');
|
|
2478
2564
|
}).join('');
|
|
2479
2565
|
const regex = new RegExp('^' + regexPattern + '$');
|
|
2480
2566
|
const matches = dateString.match(regex);
|
|
@@ -2600,10 +2686,10 @@ function parseTimeInstantComponents(dateString, pattern, config = {}) {
|
|
|
2600
2686
|
* @returns Partial time instant parameters that were parsed from the string
|
|
2601
2687
|
* @throws Error if the string doesn't match the pattern or contains invalid values
|
|
2602
2688
|
*/
|
|
2603
|
-
function parseTimeInstantBasicComponents(dateString, pattern) {
|
|
2689
|
+
function parseTimeInstantBasicComponents(dateString, pattern, ignoreIntlAvailability = false) {
|
|
2604
2690
|
// Check if Intl is available, if so warn the user about the existing function
|
|
2605
2691
|
const isIntlAvailable = typeof Intl !== 'undefined' && typeof Intl.DateTimeFormat !== 'undefined';
|
|
2606
|
-
if (isIntlAvailable)
|
|
2692
|
+
if (isIntlAvailable && !ignoreIntlAvailability)
|
|
2607
2693
|
console.warn('Intl is available, use parseTimeInstantComponents instead of parseTimeInstantBasicComponents.');
|
|
2608
2694
|
const result = {};
|
|
2609
2695
|
let patternIndex = 0;
|
|
@@ -3510,6 +3596,7 @@ exports.arrayIncludes = arrayIncludes;
|
|
|
3510
3596
|
exports.asError = asError;
|
|
3511
3597
|
exports.asPromise = asPromise;
|
|
3512
3598
|
exports.average = average;
|
|
3599
|
+
exports.averageBy = averageBy;
|
|
3513
3600
|
exports.awaitAtMost = awaitAtMost;
|
|
3514
3601
|
exports.capitalizeWord = capitalizeWord;
|
|
3515
3602
|
exports.clamp = clamp;
|
|
@@ -3542,6 +3629,7 @@ exports.entriesToList = entriesToList;
|
|
|
3542
3629
|
exports.extendArray = extendArray;
|
|
3543
3630
|
exports.extendArrayWith = extendArrayWith;
|
|
3544
3631
|
exports.fill = fill;
|
|
3632
|
+
exports.fillWith = fillWith;
|
|
3545
3633
|
exports.filterMap = filterMap;
|
|
3546
3634
|
exports.filterMapReduce = filterMapReduce;
|
|
3547
3635
|
exports.filterWithTypePredicate = filterWithTypePredicate;
|