ts-data-forge 1.5.2 → 2.0.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.
Files changed (37) hide show
  1. package/README.md +13 -1
  2. package/dist/array/array-utils.d.mts +661 -166
  3. package/dist/array/array-utils.d.mts.map +1 -1
  4. package/dist/array/array-utils.mjs +719 -79
  5. package/dist/array/array-utils.mjs.map +1 -1
  6. package/dist/array/index.d.mts +0 -1
  7. package/dist/array/index.d.mts.map +1 -1
  8. package/dist/array/index.mjs +0 -1
  9. package/dist/array/index.mjs.map +1 -1
  10. package/dist/collections/queue.mjs +0 -1
  11. package/dist/collections/queue.mjs.map +1 -1
  12. package/dist/collections/stack.mjs +4 -5
  13. package/dist/collections/stack.mjs.map +1 -1
  14. package/dist/globals.d.mts +10 -9
  15. package/dist/index.mjs +0 -1
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/json/json.mjs +0 -1
  18. package/dist/json/json.mjs.map +1 -1
  19. package/dist/number/num.d.mts +1 -1
  20. package/package.json +2 -2
  21. package/src/array/array-utils-modification.test.mts +93 -67
  22. package/src/array/array-utils-overload-type-error.test.mts +2 -2
  23. package/src/array/array-utils-reducing-value.test.mts +31 -37
  24. package/src/array/array-utils-slice-clamped.test.mts +94 -70
  25. package/src/array/array-utils-transformation.test.mts +557 -10
  26. package/src/array/array-utils.mts +1457 -516
  27. package/src/array/index.mts +0 -1
  28. package/src/collections/queue.mts +2 -2
  29. package/src/collections/stack.mts +8 -8
  30. package/src/globals.d.mts +10 -9
  31. package/src/number/num.mts +1 -1
  32. package/dist/array/tuple-utils.d.mts +0 -407
  33. package/dist/array/tuple-utils.d.mts.map +0 -1
  34. package/dist/array/tuple-utils.mjs +0 -345
  35. package/dist/array/tuple-utils.mjs.map +0 -1
  36. package/src/array/tuple-utils.mts +0 -498
  37. package/src/array/tuple-utils.test.mts +0 -518
@@ -2,7 +2,6 @@ import '../collections/imap-mapped.mjs';
2
2
  import { IMap } from '../collections/imap.mjs';
3
3
  import '../collections/iset-mapped.mjs';
4
4
  import '../collections/iset.mjs';
5
- import './tuple-utils.mjs';
6
5
  import { isString, isUndefined } from '../guard/is-type.mjs';
7
6
  import { Optional } from '../functional/optional.mjs';
8
7
  import { pipe } from '../functional/pipe.mjs';
@@ -52,11 +51,90 @@ import { range } from '../iterator/range.mjs';
52
51
  */
53
52
  var Arr;
54
53
  (function (Arr) {
55
- function size(array) {
56
- return asUint32(array.length);
57
- }
58
- Arr.size = size;
59
- Arr.length = size;
54
+ /**
55
+ * Returns the size (length) of an array as a type-safe branded integer.
56
+ *
57
+ * This function provides the array length with enhanced type safety through branded types:
58
+ * - For arrays known to be non-empty at compile time: returns `PositiveNumber & SizeType.Arr`
59
+ * - For general arrays that may be empty: returns `SizeType.Arr` (branded Uint32)
60
+ *
61
+ * The returned value is always a non-negative integer that can be safely used for array indexing
62
+ * and size comparisons. The branded type prevents common integer overflow issues and provides
63
+ * better type checking than plain numbers.
64
+ *
65
+ * @template Ar The exact type of the input array, used for precise return type inference.
66
+ * @param array The array to measure. Can be any readonly array type.
67
+ * @returns The length of the array as a branded type:
68
+ * - `IntersectBrand<PositiveNumber, SizeType.Arr>` for known non-empty arrays
69
+ * - `SizeType.Arr` for general arrays (branded Uint32, may be 0)
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * // Known non-empty arrays get positive branded type
74
+ * const tuple = [1, 2, 3] as const;
75
+ * const tupleSize = Arr.size(tuple);
76
+ * // Type: IntersectBrand<PositiveNumber, SizeType.Arr>
77
+ * // Value: 3 (branded, guaranteed positive)
78
+ *
79
+ * const nonEmpty: NonEmptyArray<string> = ['a', 'b'];
80
+ * const nonEmptySize = Arr.size(nonEmpty);
81
+ * // Type: IntersectBrand<PositiveNumber, SizeType.Arr>
82
+ * // Guaranteed to be > 0
83
+ *
84
+ * // General arrays may be empty, get regular branded type
85
+ * const generalArray: number[] = [1, 2, 3];
86
+ * const generalSize = Arr.size(generalArray);
87
+ * // Type: SizeType.Arr (branded Uint32)
88
+ * // May be 0 or positive
89
+ *
90
+ * // Empty arrays
91
+ * const emptyArray = [] as const;
92
+ * const emptySize = Arr.size(emptyArray);
93
+ * // Type: SizeType.Arr
94
+ * // Value: 0 (branded)
95
+ *
96
+ * // Runtime arrays with unknown content
97
+ * const dynamicArray = Array.from({ length: Math.random() * 10 }, (_, i) => i);
98
+ * const dynamicSize = Arr.size(dynamicArray);
99
+ * // Type: SizeType.Arr (may be 0)
100
+ *
101
+ * // Using size for safe operations
102
+ * const data = [10, 20, 30];
103
+ * const dataSize = Arr.size(data);
104
+ *
105
+ * // Safe for array creation
106
+ * const indices = Arr.seq(dataSize); // Creates [0, 1, 2]
107
+ * const zeros = Arr.zeros(dataSize); // Creates [0, 0, 0]
108
+ *
109
+ * // Functional composition
110
+ * const arrays = [
111
+ * [1, 2],
112
+ * [3, 4, 5],
113
+ * [],
114
+ * [6]
115
+ * ];
116
+ * const sizes = arrays.map(Arr.size); // [2, 3, 0, 1] (all branded)
117
+ * const totalElements = sizes.reduce(Uint32.add, 0); // 6
118
+ *
119
+ * // Type guards work with size
120
+ * if (Arr.size(data) > 0) {
121
+ * // TypeScript knows data is non-empty here
122
+ * const firstElement = data[0]; // Safe access
123
+ * }
124
+ *
125
+ * // Type inference examples
126
+ * expectType<typeof tupleSize, IntersectBrand<PositiveNumber, SizeType.Arr>>('=');
127
+ * expectType<typeof generalSize, SizeType.Arr>('=');
128
+ * expectType<typeof emptySize, SizeType.Arr>('=');
129
+ * ```
130
+ *
131
+ * @see {@link length} - Alias for this function
132
+ * @see {@link isEmpty} for checking if size is 0
133
+ * @see {@link isNonEmpty} for checking if size > 0
134
+ */
135
+ Arr.size = (array) =>
136
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
137
+ array.length;
60
138
  // type guard
61
139
  /**
62
140
  * Type guard that checks if a value is an array, excluding types that cannot be arrays.
@@ -106,26 +184,11 @@ var Arr;
106
184
  * if (Arr.isEmpty(arr)) {
107
185
  * // arr is now typed as readonly []
108
186
  * console.log('Array is empty');
187
+ * // arr[0]; // type error!
109
188
  * return 0;
110
- * } else {
111
- * // arr is now typed as NonEmptyArray<number>
112
- * return arr[0]; // Safe access - TypeScript knows it's non-empty
113
189
  * }
114
190
  * }
115
191
  *
116
- * // Conditional processing
117
- * const data = [10, 20, 30];
118
- * if (!Arr.isEmpty(data)) {
119
- * // Safe to access elements
120
- * const firstElement = data[0]; // No undefined risk
121
- * const lastElement = data[data.length - 1];
122
- * }
123
- *
124
- * // Filtering empty arrays
125
- * const arrayList: readonly number[][] = [[1, 2], [], [3], []];
126
- * const nonEmptyArrays = arrayList.filter(arr => !Arr.isEmpty(arr));
127
- * // nonEmptyArrays: [[1, 2], [3]]
128
- *
129
192
  * // Early returns
130
193
  * function sumArray(numbers: readonly number[]): number {
131
194
  * if (Arr.isEmpty(numbers)) {
@@ -134,12 +197,6 @@ var Arr;
134
197
  * return numbers.reduce((sum, n) => sum + n, 0);
135
198
  * }
136
199
  *
137
- * // Type inference examples
138
- * const testEmpty = [] as const;
139
- * const testNonEmpty = [1, 2] as const;
140
- *
141
- * expectType<Parameters<typeof Arr.isEmpty>[0], readonly unknown[]>('=');
142
- * expectType<ReturnType<typeof Arr.isEmpty>, boolean>('=');
143
200
  * ```
144
201
  *
145
202
  * @see {@link isNonEmpty} for the opposite check (non-empty arrays)
@@ -180,16 +237,14 @@ var Arr;
180
237
  *
181
238
  * // Safe operations on non-empty arrays
182
239
  * function processData(data: readonly string[]) {
183
- * if (Arr.isNonEmpty(data)) {
184
- * // All of these are now safe without undefined checks
185
- * const first = data[0];
186
- * const last = data[data.length - 1];
187
- * const middle = data[Math.floor(data.length / 2)];
188
- *
189
- * // Can safely use non-empty array methods
190
- * const joined = data.join(', ');
191
- * const reduced = data.reduce((acc, item) => acc + item.length, 0);
192
- * }
240
+ * if (!Arr.isNonEmpty(data)) return; // early return pattern
241
+ *
242
+ * // This is now safe without undefined checks
243
+ * const first = data[0];
244
+ *
245
+ * // Can safely use non-empty array methods
246
+ * const lastElement = Arr.last(data);
247
+ *
193
248
  * }
194
249
  *
195
250
  * // Filtering and working with arrays
@@ -214,7 +269,7 @@ var Arr;
214
269
  * }
215
270
  *
216
271
  * // numbers is now NonEmptyArray<number>
217
- * return numbers.reduce((sum, n) => sum + n, 0) / numbers.length;
272
+ * return Arr.sum(numbers) / Arr.size(numbers);
218
273
  * }
219
274
  *
220
275
  * // Functional composition
@@ -309,19 +364,141 @@ var Arr;
309
364
  * ```
310
365
  */
311
366
  Arr.indexIsInRange = (array, index) => Num.isInRange(0, array.length)(index);
312
- function zeros(len) {
313
- return Array.from({ length: len }).fill(0);
314
- }
315
- Arr.zeros = zeros;
316
- function seq(len) {
317
- return Array.from({ length: len }, (_, i) => asUint32(i));
318
- }
319
- Arr.seq = seq;
320
- function create(len, init) {
321
- return Array.from({ length: Math.max(0, len) }, () => init);
322
- }
323
- Arr.create = create;
324
- Arr.newArray = create;
367
+ // array creation
368
+ /**
369
+ * Creates an array of zeros with the specified length.
370
+ *
371
+ * This function provides compile-time type safety with precise return types:
372
+ * - When `len` is a compile-time known `SmallUint` (0-100), returns a tuple with exact length
373
+ * - When `len` is a positive runtime value, returns a `NonEmptyArray<0>`
374
+ * - Otherwise, returns a `readonly 0[]` that may be empty
375
+ *
376
+ * @template N The type of the length parameter. When a `SmallUint` literal is provided,
377
+ * the return type will be a tuple of exactly that length filled with zeros.
378
+ * @param len The length of the array to create. Must be a non-negative integer.
379
+ * @returns An immutable array of zeros. The exact return type depends on the input:
380
+ * - `ArrayOfLength<N, 0>` when `N` is a `SmallUint` literal
381
+ * - `NonEmptyArray<0>` when `len` is a positive runtime value
382
+ * - `readonly 0[]` for general non-negative values
383
+ *
384
+ * @example
385
+ * ```typescript
386
+ * // Compile-time known lengths produce precise tuple types
387
+ * const exactLength = Arr.zeros(3); // readonly [0, 0, 0]
388
+ * const empty = Arr.zeros(0); // readonly []
389
+ *
390
+ * // Runtime positive values produce non-empty arrays
391
+ * const count = Math.floor(Math.random() * 5) + 1;
392
+ * const nonEmpty = Arr.zeros(count); // NonEmptyArray<0>
393
+ *
394
+ * // General runtime values may be empty
395
+ * const maybeEmpty = Arr.zeros(Math.floor(Math.random() * 5)); // readonly 0[]
396
+ *
397
+ * // Type inference examples
398
+ * expectType<typeof exactLength, readonly [0, 0, 0]>('=');
399
+ * expectType<typeof empty, readonly []>('=');
400
+ * expectType<typeof nonEmpty, NonEmptyArray<0>>('=');
401
+ * expectType<typeof maybeEmpty, readonly 0[]>('=');
402
+ * ```
403
+ */
404
+ Arr.zeros = (len) =>
405
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
406
+ Array.from({ length: len }).fill(0);
407
+ /**
408
+ * Creates a sequence of consecutive integers from 0 to `len-1`.
409
+ *
410
+ * This function generates index sequences with precise compile-time typing:
411
+ * - When `len` is a compile-time known `SmallUint` (0-100), returns a tuple of consecutive integers
412
+ * - When `len` is a positive runtime value, returns a `NonEmptyArray<SizeType.Arr>`
413
+ * - Otherwise, returns a `readonly SizeType.Arr[]` that may be empty
414
+ *
415
+ * @template N The type of the length parameter. When a `SmallUint` literal is provided,
416
+ * the return type will be a tuple containing the sequence [0, 1, 2, ..., N-1].
417
+ * @param len The length of the sequence to create. Must be a non-negative integer.
418
+ * @returns An immutable array containing the sequence [0, 1, 2, ..., len-1].
419
+ * The exact return type depends on the input:
420
+ * - `Seq<N>` (precise tuple) when `N` is a `SmallUint` literal
421
+ * - `NonEmptyArray<SizeType.Arr>` when `len` is a positive runtime value
422
+ * - `readonly SizeType.Arr[]` for general non-negative values
423
+ *
424
+ * @example
425
+ * ```typescript
426
+ * // Compile-time known lengths produce precise tuple types
427
+ * const indices = Arr.seq(4); // readonly [0, 1, 2, 3]
428
+ * const empty = Arr.seq(0); // readonly []
429
+ * const single = Arr.seq(1); // readonly [0]
430
+ *
431
+ * // Runtime positive values produce non-empty arrays
432
+ * const count = Math.floor(Math.random() * 5) + 1;
433
+ * const nonEmpty = Arr.seq(count); // NonEmptyArray<SizeType.Arr>
434
+ *
435
+ * // General runtime values may be empty
436
+ * const maybeEmpty = Arr.seq(Math.floor(Math.random() * 5)); // readonly SizeType.Arr[]
437
+ *
438
+ * // Useful for generating array indices
439
+ * const data = ['a', 'b', 'c', 'd'];
440
+ * const indexSequence = Arr.seq(data.length); // [0, 1, 2, 3]
441
+ *
442
+ * // Type inference examples
443
+ * expectType<typeof indices, readonly [0, 1, 2, 3]>('=');
444
+ * expectType<typeof empty, readonly []>('=');
445
+ * expectType<typeof single, readonly [0]>('=');
446
+ * ```
447
+ */
448
+ Arr.seq = (len) =>
449
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
450
+ Array.from({ length: len }, (_, i) => i);
451
+ /**
452
+ * Creates a new array of the specified length, with each position filled with the provided initial value.
453
+ *
454
+ * This function provides compile-time type safety with precise return types and performs shallow copying
455
+ * of the initial value (the same reference is used for all positions):
456
+ * - When `len` is a compile-time known `SmallUint` (0-100), returns a tuple of exactly that length
457
+ * - When `len` is a positive runtime value, returns a `NonEmptyArray<V>`
458
+ * - Otherwise, returns a `readonly V[]` that may be empty
459
+ *
460
+ * @template V The type of the initial value. The `const` constraint preserves literal types.
461
+ * @template N The type of the length parameter when it's a `SmallUint` literal.
462
+ * @param len The length of the array to create. Must be a non-negative integer.
463
+ * @param init The value to fill each position with. The same reference is used for all positions.
464
+ * @returns An immutable array filled with the initial value. The exact return type depends on the length:
465
+ * - `ArrayOfLength<N, V>` when `len` is a `SmallUint` literal
466
+ * - `NonEmptyArray<V>` when `len` is a positive runtime value
467
+ * - `readonly V[]` for general non-negative values
468
+ *
469
+ * @example
470
+ * ```typescript
471
+ * // Compile-time known lengths produce precise tuple types
472
+ * const strings = Arr.create(3, 'hello'); // readonly ['hello', 'hello', 'hello']
473
+ * const numbers = Arr.create(2, 42); // readonly [42, 42]
474
+ * const empty = Arr.create(0, 'unused'); // readonly []
475
+ *
476
+ * // Object references are shared (shallow copy behavior)
477
+ * const obj = { id: 1, name: 'test' };
478
+ * const objects = Arr.create(3, obj); // readonly [obj, obj, obj]
479
+ * objects[0] === objects[1]; // true - same reference
480
+ * objects[0].id = 999; // Mutates the shared object
481
+ * console.log(objects[1].id); // 999 - all positions affected
482
+ *
483
+ * // Runtime positive values produce non-empty arrays
484
+ * const count = Math.floor(Math.random() * 5) + 1;
485
+ * const nonEmpty = Arr.create(count, 'item'); // NonEmptyArray<string>
486
+ *
487
+ * // Literal type preservation with const assertion
488
+ * const literals = Arr.create(2, 'success' as const); // readonly ['success', 'success']
489
+ *
490
+ * // Type inference examples
491
+ * expectType<typeof strings, readonly ['hello', 'hello', 'hello']>('=');
492
+ * expectType<typeof numbers, readonly [42, 42]>('=');
493
+ * expectType<typeof empty, readonly []>('=');
494
+ * ```
495
+ *
496
+ * @see {@link zeros} for creating arrays filled with zeros
497
+ * @see {@link seq} for creating sequences of consecutive integers
498
+ */
499
+ Arr.create = (len, init) =>
500
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
501
+ Array.from({ length: Math.max(0, len) }, () => init);
325
502
  /**
326
503
  * Creates an array from a generator function.
327
504
  *
@@ -429,16 +606,163 @@ var Arr;
429
606
  }
430
607
  }
431
608
  Arr.at = at;
432
- function head(array) {
433
- const element = array.at(0);
434
- return element === undefined ? Optional.none : Optional.some(element);
435
- }
436
- Arr.head = head;
437
- function last(array) {
438
- const element = array.at(-1);
439
- return element === undefined ? Optional.none : Optional.some(element);
440
- }
441
- Arr.last = last;
609
+ /**
610
+ * Returns the first element of an array wrapped in an Optional.
611
+ *
612
+ * This function provides type-safe access to the first element with precise return types:
613
+ * - For empty arrays: returns `Optional.None`
614
+ * - For tuples with known first element: returns `Optional.Some<FirstElementType>`
615
+ * - For non-empty arrays: returns `Optional.Some<ElementType>`
616
+ * - For general arrays: returns `Optional<ElementType>`
617
+ *
618
+ * The function leverages TypeScript's type system to provide the most precise return type
619
+ * based on the input array type, making it safer than direct indexing.
620
+ *
621
+ * @template E The type of elements in the array.
622
+ * @param array The array to get the first element from.
623
+ * @returns An Optional containing the first element:
624
+ * - `Optional.None` if the array is empty
625
+ * - `Optional.Some<E>` containing the first element if the array is non-empty
626
+ *
627
+ * @example
628
+ * ```typescript
629
+ * // Empty array - precise None type
630
+ * const emptyResult = Arr.head([]); // Optional.None
631
+ * console.log(Optional.isNone(emptyResult)); // true
632
+ *
633
+ * // Tuple with known structure - precise Some type
634
+ * const tupleResult = Arr.head(['first', 'second', 'third'] as const);
635
+ * // Type: Optional.Some<'first'>
636
+ * if (Optional.isSome(tupleResult)) {
637
+ * console.log(tupleResult.value); // 'first' - TypeScript knows exact type
638
+ * }
639
+ *
640
+ * // Non-empty array - guaranteed Some type
641
+ * const nonEmpty: NonEmptyArray<number> = [10, 20, 30] as NonEmptyArray<number>;
642
+ * const guaranteedResult = Arr.head(nonEmpty); // Optional.Some<number>
643
+ * // No need to check - always Some for NonEmptyArray
644
+ *
645
+ * // General array - may be Some or None
646
+ * const generalArray: number[] = [1, 2, 3];
647
+ * const maybeResult = Arr.head(generalArray); // Optional<number>
648
+ * if (Optional.isSome(maybeResult)) {
649
+ * console.log(`First element: ${maybeResult.value}`);
650
+ * } else {
651
+ * console.log('Array is empty');
652
+ * }
653
+ *
654
+ * // Working with different types
655
+ * const strings = ['hello', 'world'];
656
+ * const firstString = Arr.head(strings); // Optional<string>
657
+ *
658
+ * const objects = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
659
+ * const firstObject = Arr.head(objects); // Optional<{id: number, name: string}>
660
+ *
661
+ * // Functional composition
662
+ * const getFirstElements = (arrays: readonly number[][]) =>
663
+ * arrays.map(Arr.head).filter(Optional.isSome);
664
+ *
665
+ * const nestedArrays = [[1, 2], [3, 4], [], [5]];
666
+ * const firstElements = getFirstElements(nestedArrays);
667
+ * // [Optional.Some(1), Optional.Some(3), Optional.Some(5)]
668
+ *
669
+ * // Type inference examples
670
+ * expectType<typeof emptyResult, Optional.None>('=');
671
+ * expectType<typeof tupleResult, Optional.Some<'first'>>('=');
672
+ * expectType<typeof guaranteedResult, Optional.Some<number>>('=');
673
+ * expectType<typeof maybeResult, Optional<number>>('=');
674
+ * ```
675
+ *
676
+ * @see {@link last} for getting the last element
677
+ * @see {@link at} for accessing elements at specific indices
678
+ * @see {@link tail} for getting all elements except the first
679
+ */
680
+ Arr.head = (array) =>
681
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
682
+ (array.length === 0 ? Optional.none : Optional.some(array.at(0)));
683
+ /**
684
+ * Returns the last element of an array wrapped in an Optional.
685
+ *
686
+ * This function provides type-safe access to the last element with precise return types:
687
+ * - For empty arrays: returns `Optional.None`
688
+ * - For tuples with known last element: returns `Optional.Some<LastElementType>`
689
+ * - For non-empty arrays: returns `Optional.Some<ElementType>`
690
+ * - For general arrays: returns `Optional<ElementType>`
691
+ *
692
+ * The function leverages TypeScript's type system to provide the most precise return type
693
+ * based on the input array type, making it safer than direct indexing.
694
+ *
695
+ * @template E The type of elements in the array.
696
+ * @param array The array to get the last element from.
697
+ * @returns An Optional containing the last element:
698
+ * - `Optional.None` if the array is empty
699
+ * - `Optional.Some<E>` containing the last element if the array is non-empty
700
+ *
701
+ * @example
702
+ * ```typescript
703
+ * // Empty array - precise None type
704
+ * const emptyResult = Arr.last([]); // Optional.None
705
+ * console.log(Optional.isNone(emptyResult)); // true
706
+ *
707
+ * // Tuple with known structure - precise Some type
708
+ * const tupleResult = Arr.last(['first', 'middle', 'last'] as const);
709
+ * // Type: Optional.Some<'last'>
710
+ * if (Optional.isSome(tupleResult)) {
711
+ * console.log(tupleResult.value); // 'last' - TypeScript knows exact type
712
+ * }
713
+ *
714
+ * // Non-empty array - guaranteed Some type
715
+ * const nonEmpty: NonEmptyArray<number> = [10, 20, 30] as NonEmptyArray<number>;
716
+ * const guaranteedResult = Arr.last(nonEmpty); // Optional.Some<number>
717
+ * // No need to check - always Some for NonEmptyArray
718
+ *
719
+ * // General array - may be Some or None
720
+ * const generalArray: number[] = [1, 2, 3];
721
+ * const maybeResult = Arr.last(generalArray); // Optional<number>
722
+ * if (Optional.isSome(maybeResult)) {
723
+ * console.log(`Last element: ${maybeResult.value}`);
724
+ * } else {
725
+ * console.log('Array is empty');
726
+ * }
727
+ *
728
+ * // Working with different types
729
+ * const strings = ['hello', 'world', 'example'];
730
+ * const lastString = Arr.last(strings); // Optional<string>
731
+ *
732
+ * const coordinates = [{x: 0, y: 0}, {x: 1, y: 1}, {x: 2, y: 2}];
733
+ * const lastCoordinate = Arr.last(coordinates); // Optional<{x: number, y: number}>
734
+ *
735
+ * // Single element arrays
736
+ * const single = [42];
737
+ * const singleResult = Arr.last(single); // Optional<number> containing 42
738
+ *
739
+ * // Functional composition with arrays of arrays
740
+ * const getLastElements = (arrays: readonly string[][]) =>
741
+ * arrays.map(Arr.last).filter(Optional.isSome);
742
+ *
743
+ * const nestedArrays = [['a', 'b'], ['c'], [], ['d', 'e', 'f']];
744
+ * const lastElements = getLastElements(nestedArrays);
745
+ * // [Optional.Some('b'), Optional.Some('c'), Optional.Some('f')]
746
+ *
747
+ * // Common pattern: get last element or default
748
+ * const data = [10, 20, 30];
749
+ * const lastOrDefault = Optional.unwrapOr(Arr.last(data), 0); // 30
750
+ * const emptyLastOrDefault = Optional.unwrapOr(Arr.last([]), 0); // 0
751
+ *
752
+ * // Type inference examples
753
+ * expectType<typeof emptyResult, Optional.None>('=');
754
+ * expectType<typeof tupleResult, Optional.Some<'last'>>('=');
755
+ * expectType<typeof guaranteedResult, Optional.Some<number>>('=');
756
+ * expectType<typeof maybeResult, Optional<number>>('=');
757
+ * ```
758
+ *
759
+ * @see {@link head} for getting the first element
760
+ * @see {@link at} for accessing elements at specific indices with negative indexing support
761
+ * @see {@link butLast} for getting all elements except the last
762
+ */
763
+ Arr.last = (array) =>
764
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
765
+ (array.length === 0 ? Optional.none : Optional.some(array.at(-1)));
442
766
  function sliceClamped(...args) {
443
767
  switch (args.length) {
444
768
  case 3: {
@@ -502,7 +826,7 @@ var Arr;
502
826
  switch (args.length) {
503
827
  case 2: {
504
828
  const [array, num] = args;
505
- return sliceClamped(array, Uint32.sub(size(array), num), size(array));
829
+ return sliceClamped(array, Uint32.sub(Arr.size(array), num), Arr.size(array));
506
830
  }
507
831
  case 1: {
508
832
  const [num] = args;
@@ -515,7 +839,7 @@ var Arr;
515
839
  switch (args.length) {
516
840
  case 2: {
517
841
  const [array, num] = args;
518
- return sliceClamped(array, num, size(array));
842
+ return sliceClamped(array, num, Arr.size(array));
519
843
  }
520
844
  case 1: {
521
845
  const [num] = args;
@@ -528,7 +852,7 @@ var Arr;
528
852
  switch (args.length) {
529
853
  case 2: {
530
854
  const [array, num] = args;
531
- return sliceClamped(array, 0, Uint32.sub(size(array), num));
855
+ return sliceClamped(array, 0, Uint32.sub(Arr.size(array), num));
532
856
  }
533
857
  case 1: {
534
858
  const [num] = args;
@@ -537,14 +861,26 @@ var Arr;
537
861
  }
538
862
  }
539
863
  Arr.skipLast = skipLast;
864
+ function set(...args) {
865
+ switch (args.length) {
866
+ case 3: {
867
+ const [array, index, newValue] = args;
868
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
869
+ return array.with(index, newValue);
870
+ }
871
+ case 2: {
872
+ const [index, newValue] = args;
873
+ return (array) => set(array, index, newValue);
874
+ }
875
+ }
876
+ }
877
+ Arr.set = set;
540
878
  function toUpdated(...args) {
541
879
  switch (args.length) {
542
880
  case 3: {
543
881
  const [array, index, updater] = args;
544
- return index < 0 || index >= array.length
545
- ? array // Return a copy if index is out of bounds
546
- : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-unsafe-type-assertion
547
- array.with(index, updater(array[index]));
882
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-unsafe-type-assertion
883
+ return array.with(index, updater(array[index]));
548
884
  }
549
885
  case 2: {
550
886
  const [index, updater] = args;
@@ -612,9 +948,7 @@ var Arr;
612
948
  switch (args.length) {
613
949
  case 2: {
614
950
  const [array, value] = args;
615
- const cp = castMutable(Arr.copy(array));
616
- cp.fill(value);
617
- return cp;
951
+ return Arr.create(asPositiveUint32(array.length), value);
618
952
  }
619
953
  case 1: {
620
954
  const [value] = args;
@@ -657,6 +991,25 @@ var Arr;
657
991
  }
658
992
  }
659
993
  Arr.find = find;
994
+ function findLast(...args) {
995
+ switch (args.length) {
996
+ case 2: {
997
+ const [array, predicate] = args;
998
+ const foundIndex = array.findLastIndex(
999
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1000
+ predicate);
1001
+ return foundIndex === -1
1002
+ ? Optional.none
1003
+ : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1004
+ Optional.some(array[foundIndex]);
1005
+ }
1006
+ case 1: {
1007
+ const [predicate] = args;
1008
+ return (array) => findLast(array, predicate);
1009
+ }
1010
+ }
1011
+ }
1012
+ Arr.findLast = findLast;
660
1013
  function findIndex(...args) {
661
1014
  switch (args.length) {
662
1015
  case 2: {
@@ -672,6 +1025,21 @@ var Arr;
672
1025
  }
673
1026
  }
674
1027
  Arr.findIndex = findIndex;
1028
+ function findLastIndex(...args) {
1029
+ switch (args.length) {
1030
+ case 2: {
1031
+ const [array, predicate] = args;
1032
+ return pipe(array.findLastIndex(
1033
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1034
+ predicate)).map((idx) => (idx >= 0 ? asUint32(idx) : -1)).value;
1035
+ }
1036
+ case 1: {
1037
+ const [predicate] = args;
1038
+ return (array) => findLastIndex(array, predicate);
1039
+ }
1040
+ }
1041
+ }
1042
+ Arr.findLastIndex = findLastIndex;
675
1043
  function indexOf(...args) {
676
1044
  switch (args.length) {
677
1045
  case 2: {
@@ -728,6 +1096,32 @@ var Arr;
728
1096
  }
729
1097
  }
730
1098
  Arr.lastIndexOfFrom = lastIndexOfFrom;
1099
+ function every(...args) {
1100
+ switch (args.length) {
1101
+ case 2: {
1102
+ const [array, predicate] = args;
1103
+ return array.every((a, i) => predicate(a, asUint32(i)));
1104
+ }
1105
+ case 1: {
1106
+ const [predicate] = args;
1107
+ return (array) => every(array, predicate);
1108
+ }
1109
+ }
1110
+ }
1111
+ Arr.every = every;
1112
+ function some(...args) {
1113
+ switch (args.length) {
1114
+ case 2: {
1115
+ const [array, predicate] = args;
1116
+ return array.some((a, i) => predicate(a, asUint32(i)));
1117
+ }
1118
+ case 1: {
1119
+ const [predicate] = args;
1120
+ return (array) => some(array, predicate);
1121
+ }
1122
+ }
1123
+ }
1124
+ Arr.some = some;
731
1125
  function foldl(...args) {
732
1126
  switch (args.length) {
733
1127
  case 3: {
@@ -868,10 +1262,37 @@ var Arr;
868
1262
  */
869
1263
  Arr.zip = (array1, array2) =>
870
1264
  // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
871
- seq(Uint32.min(size(array1), size(array2))).map((i) =>
1265
+ Arr.seq(Uint32.min(Arr.size(array1), Arr.size(array2))).map((i) =>
872
1266
  // Non-null assertion is safe here because `i` is always within bounds of both arrays up to the length of the shorter one.
873
1267
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
874
1268
  tp(array1[i], array2[i]));
1269
+ function map(...args) {
1270
+ switch (args.length) {
1271
+ case 2: {
1272
+ const [array, mapFn] = args;
1273
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1274
+ return array.map(mapFn);
1275
+ }
1276
+ case 1: {
1277
+ const [mapFn] = args;
1278
+ return (array) => map(array, mapFn);
1279
+ }
1280
+ }
1281
+ }
1282
+ Arr.map = map;
1283
+ function filter(...args) {
1284
+ switch (args.length) {
1285
+ case 2: {
1286
+ const [array, predicate] = args;
1287
+ return array.filter((a, i) => predicate(a, asUint32(i)));
1288
+ }
1289
+ case 1: {
1290
+ const [predicate] = args;
1291
+ return (array) => filter(array, predicate);
1292
+ }
1293
+ }
1294
+ }
1295
+ Arr.filter = filter;
875
1296
  function filterNot(...args) {
876
1297
  switch (args.length) {
877
1298
  case 2: {
@@ -885,6 +1306,43 @@ var Arr;
885
1306
  }
886
1307
  }
887
1308
  Arr.filterNot = filterNot;
1309
+ function flat(...args) {
1310
+ switch (args.length) {
1311
+ case 2: {
1312
+ const [array, depth] = args;
1313
+ return array.flat(depth);
1314
+ }
1315
+ case 1: {
1316
+ const [arrayOrDepth] = args;
1317
+ if (typeof arrayOrDepth === 'number') {
1318
+ const depth = arrayOrDepth;
1319
+ return (array) => flat(array, depth);
1320
+ }
1321
+ else if (arrayOrDepth === undefined) {
1322
+ return (array) => flat(array, 1);
1323
+ }
1324
+ else {
1325
+ return arrayOrDepth.flat(1);
1326
+ }
1327
+ }
1328
+ case 0:
1329
+ return (array) => flat(array, 1);
1330
+ }
1331
+ }
1332
+ Arr.flat = flat;
1333
+ function flatMap(...args) {
1334
+ switch (args.length) {
1335
+ case 2: {
1336
+ const [array, mapFn] = args;
1337
+ return array.flatMap((a, i) => mapFn(a, asUint32(i)));
1338
+ }
1339
+ case 1: {
1340
+ const [mapFn] = args;
1341
+ return (array) => flatMap(array, mapFn);
1342
+ }
1343
+ }
1344
+ }
1345
+ Arr.flatMap = flatMap;
888
1346
  /**
889
1347
  * Concatenates two arrays.
890
1348
  * @template E1 The type of the first array (can be a tuple).
@@ -906,7 +1364,7 @@ var Arr;
906
1364
  const [array, chunkSize] = args;
907
1365
  return chunkSize < 2
908
1366
  ? []
909
- : seq(asUint32(Math.ceil(array.length / chunkSize))).map((i) => array.slice(chunkSize * i, chunkSize * (i + 1)));
1367
+ : Arr.seq(asUint32(Math.ceil(array.length / chunkSize))).map((i) => array.slice(chunkSize * i, chunkSize * (i + 1)));
910
1368
  }
911
1369
  case 1: {
912
1370
  const [chunkSize] = args;
@@ -915,6 +1373,61 @@ var Arr;
915
1373
  }
916
1374
  }
917
1375
  Arr.partition = partition;
1376
+ /**
1377
+ * Reverses a tuple, preserving element types in their new positions.
1378
+ *
1379
+ * The type system precisely tracks the reversal, so the returned tuple
1380
+ * has its element types in the exact reverse order. This is more precise
1381
+ * than array reversal which loses positional type information.
1382
+ *
1383
+ * @template T - The tuple type to reverse
1384
+ * @param array - The input tuple
1385
+ * @returns A new tuple with elements in reverse order and precise typing
1386
+ *
1387
+ * @example
1388
+ * ```typescript
1389
+ * // Basic reversal
1390
+ * const nums = [1, 2, 3] as const;
1391
+ * const reversed = Arr.toReversed(nums); // readonly [3, 2, 1]
1392
+ *
1393
+ * // Mixed types preserved in reverse positions
1394
+ * const mixed = [1, 'hello', true, null] as const;
1395
+ * const revMixed = Arr.toReversed(mixed);
1396
+ * // readonly [null, true, 'hello', 1]
1397
+ *
1398
+ * // Empty and single-element tuples
1399
+ * const empty = [] as const;
1400
+ * const revEmpty = Arr.toReversed(empty); // readonly []
1401
+ * const single = [42] as const;
1402
+ * const revSingle = Arr.toReversed(single); // readonly [42]
1403
+ * ```
1404
+ */
1405
+ Arr.toReversed = (array) =>
1406
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1407
+ array.toReversed();
1408
+ /**
1409
+ * Sorts an array by a value derived from its elements, using a numeric mapping.
1410
+ * @template E The type of elements in the array.
1411
+ * @param array The input array.
1412
+ * @param comparatorValueMapper A function `(value: A) => number` that maps an element to a number for comparison.
1413
+ * @param comparator An optional custom comparator function `(x: number, y: number) => number` for the mapped numbers. Defaults to ascending sort (x - y).
1414
+ * @returns A new array sorted by the mapped values.
1415
+ * @example
1416
+ * ```ts
1417
+ * const items = [{ name: 'Eve', score: 70 }, { name: 'Adam', score: 90 }, { name: 'Bob', score: 80 }];
1418
+ * Arr.toSortedBy(items, item => item.score);
1419
+ * // [{ name: 'Eve', score: 70 }, { name: 'Bob', score: 80 }, { name: 'Adam', score: 90 }]
1420
+ * Arr.toSortedBy(items, item => item.score, (a, b) => b - a); // Sort descending
1421
+ * // [{ name: 'Adam', score: 90 }, { name: 'Bob', score: 80 }, { name: 'Eve', score: 70 }]
1422
+ * ```
1423
+ */
1424
+ Arr.toSorted = (...[array, comparator]) =>
1425
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1426
+ array.toSorted(
1427
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1428
+ comparator ??
1429
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1430
+ ((x, y) => x - y));
918
1431
  function toSortedBy(array, comparatorValueMapper, comparator) {
919
1432
  return array.toSorted((x, y) => comparator === undefined
920
1433
  ? // This branch assumes V is number if comparator is undefined.
@@ -981,9 +1494,33 @@ var Arr;
981
1494
  * Arr.uniq([1, 2, 2, 3, 1, 4]); // [1, 2, 3, 4]
982
1495
  * ```
983
1496
  */
984
- Arr.uniq = (array) => Array.from(new Set(array));
985
- function uniqBy(array, mapFn) {
1497
+ Arr.uniq = (array) =>
1498
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
1499
+ Array.from(new Set(array));
1500
+ /**
1501
+ * Creates a new array with unique elements from the input array, based on the values returned by `mapFn`.
1502
+ *
1503
+ * - If the input is a non-empty array, returns a non-empty array.
1504
+ * - Otherwise, returns a readonly array.
1505
+ *
1506
+ * @template E The type of elements in the array.
1507
+ * @template P The type of the mapped value (used for uniqueness comparison).
1508
+ * @param array The input array.
1509
+ * @param mapFn A function `(value: A) => P` to map elements to values for uniqueness comparison.
1510
+ * @returns A new array with unique elements based on the mapped values.
1511
+ * @example
1512
+ * ```ts
1513
+ * const users = [
1514
+ * { id: 1, name: 'Alice' },
1515
+ * { id: 2, name: 'Bob' },
1516
+ * { id: 1, name: 'Alicia' }, // Duplicate id
1517
+ * ];
1518
+ * Arr.uniqBy(users, user => user.id); // [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
1519
+ * ```
1520
+ */
1521
+ Arr.uniqBy = (array, mapFn) => {
986
1522
  const mut_mappedValues = new Set();
1523
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
987
1524
  return array.filter((val) => {
988
1525
  const mappedValue = mapFn(val);
989
1526
  if (mut_mappedValues.has(mappedValue))
@@ -991,8 +1528,7 @@ var Arr;
991
1528
  mut_mappedValues.add(mappedValue);
992
1529
  return true;
993
1530
  });
994
- }
995
- Arr.uniqBy = uniqBy;
1531
+ };
996
1532
  // set operations & equality
997
1533
  /**
998
1534
  * Checks if two arrays are equal by performing a shallow comparison of their elements.
@@ -1135,12 +1671,101 @@ var Arr;
1135
1671
  }
1136
1672
  return mut_result;
1137
1673
  };
1674
+ // iterators
1675
+ /**
1676
+ * Returns an iterable of key-value pairs for every entry in the array.
1677
+ *
1678
+ * This function returns an array where each element is a tuple containing the index and value.
1679
+ * The indices are branded as `SizeType.Arr` for type safety.
1680
+ *
1681
+ * @template Ar The exact type of the input array.
1682
+ * @param array The array to get entries from.
1683
+ * @returns An array of `[index, value]` pairs.
1684
+ *
1685
+ * @example
1686
+ * ```typescript
1687
+ * // Direct usage
1688
+ * const fruits = ['apple', 'banana', 'cherry'];
1689
+ * const entries = Arr.entries(fruits); // [[0, 'apple'], [1, 'banana'], [2, 'cherry']]
1690
+ *
1691
+ * // Curried usage
1692
+ * const getEntries = Arr.entries;
1693
+ * const result = getEntries(['a', 'b']); // [[0, 'a'], [1, 'b']]
1694
+ *
1695
+ * // With tuples
1696
+ * const tuple = [10, 20, 30] as const;
1697
+ * const tupleEntries = Arr.entries(tuple); // [[0, 10], [1, 20], [2, 30]]
1698
+ * ```
1699
+ */
1700
+ function* entries(array) {
1701
+ for (const [index, value] of array.entries()) {
1702
+ yield [asUint32(index), value];
1703
+ }
1704
+ }
1705
+ Arr.entries = entries;
1706
+ /**
1707
+ * Returns an iterable of values in the array.
1708
+ *
1709
+ * This function is essentially an identity function that returns a copy of the array.
1710
+ * It's included for API completeness and consistency with native Array methods.
1711
+ *
1712
+ * @template Ar The exact type of the input array.
1713
+ * @param array The array to get values from.
1714
+ * @returns A copy of the input array.
1715
+ *
1716
+ * @example
1717
+ * ```typescript
1718
+ * // Direct usage
1719
+ * const numbers = [1, 2, 3];
1720
+ * const values = Arr.values(numbers); // [1, 2, 3]
1721
+ *
1722
+ * // Curried usage
1723
+ * const getValues = Arr.values;
1724
+ * const result = getValues(['a', 'b']); // ['a', 'b']
1725
+ * ```
1726
+ */
1727
+ function* values(array) {
1728
+ for (const value of array.values()) {
1729
+ yield value;
1730
+ }
1731
+ }
1732
+ Arr.values = values;
1733
+ /**
1734
+ * Returns an iterable of keys in the array.
1735
+ *
1736
+ * This function returns an array of branded indices (`SizeType.Arr`) for type safety.
1737
+ *
1738
+ * @template Ar The exact type of the input array.
1739
+ * @param array The array to get indices from.
1740
+ * @returns An array of indices.
1741
+ *
1742
+ * @example
1743
+ * ```typescript
1744
+ * // Direct usage
1745
+ * const fruits = ['apple', 'banana', 'cherry'];
1746
+ * const indices = Arr.indices(fruits); // [0, 1, 2]
1747
+ *
1748
+ * // Curried usage
1749
+ * const getIndices = Arr.indices;
1750
+ * const result = getIndices(['a', 'b']); // [0, 1]
1751
+ *
1752
+ * // Empty array
1753
+ * const empty: string[] = [];
1754
+ * const emptyIndices = Arr.indices(empty); // []
1755
+ * ```
1756
+ */
1757
+ function* indices(array) {
1758
+ for (const key of array.keys()) {
1759
+ yield asUint32(key);
1760
+ }
1761
+ }
1762
+ Arr.indices = indices;
1138
1763
  // aliases
1139
1764
  /**
1140
1765
  * Alias for `head`. Returns the first element of an array.
1141
1766
  * @see {@link head}
1142
1767
  */
1143
- Arr.first = head;
1768
+ Arr.first = Arr.head;
1144
1769
  /**
1145
1770
  * Alias for `tail`. Returns all elements of an array except the first one.
1146
1771
  * @see {@link tail}
@@ -1166,6 +1791,21 @@ var Arr;
1166
1791
  * @see {@link partition}
1167
1792
  */
1168
1793
  Arr.chunk = partition;
1794
+ /**
1795
+ * Alias for `create`. Creates a new array of the specified length, with each position filled with the provided initial value.
1796
+ * @see {@link create}
1797
+ */
1798
+ Arr.newArray = Arr.create;
1799
+ /**
1800
+ * Alias for `size`. Returns the length of an array.
1801
+ * @see {@link size}
1802
+ */
1803
+ Arr.length = Arr.size;
1804
+ /**
1805
+ * Alias for `indices`. Returns an iterable of keys in the array.
1806
+ * @see {@link indices}
1807
+ */
1808
+ Arr.keys = indices;
1169
1809
  })(Arr || (Arr = {}));
1170
1810
 
1171
1811
  export { Arr };