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.
- package/README.md +13 -1
- package/dist/array/array-utils.d.mts +661 -166
- package/dist/array/array-utils.d.mts.map +1 -1
- package/dist/array/array-utils.mjs +719 -79
- package/dist/array/array-utils.mjs.map +1 -1
- package/dist/array/index.d.mts +0 -1
- package/dist/array/index.d.mts.map +1 -1
- package/dist/array/index.mjs +0 -1
- package/dist/array/index.mjs.map +1 -1
- package/dist/collections/queue.mjs +0 -1
- package/dist/collections/queue.mjs.map +1 -1
- package/dist/collections/stack.mjs +4 -5
- package/dist/collections/stack.mjs.map +1 -1
- package/dist/globals.d.mts +10 -9
- package/dist/index.mjs +0 -1
- package/dist/index.mjs.map +1 -1
- package/dist/json/json.mjs +0 -1
- package/dist/json/json.mjs.map +1 -1
- package/dist/number/num.d.mts +1 -1
- package/package.json +2 -2
- package/src/array/array-utils-modification.test.mts +93 -67
- package/src/array/array-utils-overload-type-error.test.mts +2 -2
- package/src/array/array-utils-reducing-value.test.mts +31 -37
- package/src/array/array-utils-slice-clamped.test.mts +94 -70
- package/src/array/array-utils-transformation.test.mts +557 -10
- package/src/array/array-utils.mts +1457 -516
- package/src/array/index.mts +0 -1
- package/src/collections/queue.mts +2 -2
- package/src/collections/stack.mts +8 -8
- package/src/globals.d.mts +10 -9
- package/src/number/num.mts +1 -1
- package/dist/array/tuple-utils.d.mts +0 -407
- package/dist/array/tuple-utils.d.mts.map +0 -1
- package/dist/array/tuple-utils.mjs +0 -345
- package/dist/array/tuple-utils.mjs.map +0 -1
- package/src/array/tuple-utils.mts +0 -498
- 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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
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
|
|
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
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
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
|
-
|
|
545
|
-
|
|
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
|
-
|
|
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) =>
|
|
985
|
-
|
|
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 };
|