ts-data-forge 7.0.0 → 7.0.1
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/dist/array/impl/array-utils-creation.d.mts.map +1 -1
- package/dist/array/impl/array-utils-creation.mjs +1 -1
- package/dist/array/impl/array-utils-creation.mjs.map +1 -1
- package/dist/array/impl/array-utils-iterators.mjs +1 -1
- package/dist/array/impl/array-utils-iterators.mjs.map +1 -1
- package/dist/array/impl/array-utils-reducing-value.d.mts +1 -1
- package/dist/array/impl/array-utils-reducing-value.d.mts.map +1 -1
- package/dist/array/impl/array-utils-reducing-value.mjs +0 -1
- package/dist/array/impl/array-utils-reducing-value.mjs.map +1 -1
- package/dist/array/impl/array-utils-search.d.mts +0 -2
- package/dist/array/impl/array-utils-search.d.mts.map +1 -1
- package/dist/array/impl/array-utils-search.mjs.map +1 -1
- package/dist/array/impl/array-utils-set-op.d.mts.map +1 -1
- package/dist/array/impl/array-utils-set-op.mjs +0 -1
- package/dist/array/impl/array-utils-set-op.mjs.map +1 -1
- package/dist/array/impl/array-utils-transformation.d.mts +1 -1
- package/dist/array/impl/array-utils-transformation.d.mts.map +1 -1
- package/dist/array/impl/array-utils-transformation.mjs +2 -4
- package/dist/array/impl/array-utils-transformation.mjs.map +1 -1
- package/dist/array/impl/array-utils-validation.d.mts.map +1 -1
- package/dist/array/impl/array-utils-validation.mjs +3 -9
- package/dist/array/impl/array-utils-validation.mjs.map +1 -1
- package/dist/collections/imap-mapped.d.mts +622 -3
- package/dist/collections/imap-mapped.d.mts.map +1 -1
- package/dist/collections/imap-mapped.mjs +111 -18
- package/dist/collections/imap-mapped.mjs.map +1 -1
- package/dist/collections/imap.d.mts +3 -3
- package/dist/collections/imap.d.mts.map +1 -1
- package/dist/collections/imap.mjs +6 -5
- package/dist/collections/imap.mjs.map +1 -1
- package/dist/collections/iset-mapped.d.mts +4 -4
- package/dist/collections/iset-mapped.d.mts.map +1 -1
- package/dist/collections/iset-mapped.mjs +8 -4
- package/dist/collections/iset-mapped.mjs.map +1 -1
- package/dist/collections/iset.d.mts +2 -2
- package/dist/collections/iset.d.mts.map +1 -1
- package/dist/collections/iset.mjs +5 -2
- package/dist/collections/iset.mjs.map +1 -1
- package/dist/collections/queue.mjs +20 -20
- package/dist/collections/queue.mjs.map +1 -1
- package/dist/collections/stack.mjs +17 -17
- package/dist/collections/stack.mjs.map +1 -1
- package/dist/functional/optional/impl/optional-flat-map.d.mts +2 -2
- package/dist/functional/pipe.d.mts.map +1 -1
- package/dist/functional/pipe.mjs.map +1 -1
- package/dist/functional/result/impl/result-flat-map.d.mts +2 -2
- package/dist/functional/ternary-result/impl/ternary-result-flat-map.d.mts +5 -3
- package/dist/functional/ternary-result/impl/ternary-result-flat-map.d.mts.map +1 -1
- package/dist/functional/ternary-result/impl/ternary-result-flat-map.mjs.map +1 -1
- package/dist/guard/has-key.d.mts.map +1 -1
- package/dist/guard/has-key.mjs +1 -3
- package/dist/guard/has-key.mjs.map +1 -1
- package/dist/guard/is-non-null-object.d.mts.map +1 -1
- package/dist/guard/is-non-null-object.mjs +1 -3
- package/dist/guard/is-non-null-object.mjs.map +1 -1
- package/dist/guard/key-is-in.d.mts.map +1 -1
- package/dist/guard/key-is-in.mjs +1 -3
- package/dist/guard/key-is-in.mjs.map +1 -1
- package/dist/json/json.mjs +5 -7
- package/dist/json/json.mjs.map +1 -1
- package/dist/number/branded-types/non-zero-safe-int.mjs +2 -2
- package/dist/number/branded-types/positive-safe-int.mjs +1 -1
- package/dist/number/branded-types/safe-int.mjs +2 -2
- package/dist/number/branded-types/safe-uint.mjs +1 -1
- package/dist/number/num-arithmetic-types.d.mts.map +1 -1
- package/dist/number/num.mjs +3 -3
- package/dist/object/object.mjs +1 -1
- package/dist/object/object.mjs.map +1 -1
- package/dist/others/fast-deep-equal.d.mts.map +1 -1
- package/dist/others/fast-deep-equal.mjs +0 -1
- package/dist/others/fast-deep-equal.mjs.map +1 -1
- package/dist/others/unknown-to-string.mjs +2 -2
- package/dist/others/unknown-to-string.mjs.map +1 -1
- package/package.json +5 -5
- package/src/array/impl/array-utils-creation.mts +1 -1
- package/src/array/impl/array-utils-iterators.mts +1 -1
- package/src/array/impl/array-utils-reducing-value.mts +1 -2
- package/src/array/impl/array-utils-search.mts +0 -2
- package/src/array/impl/array-utils-set-op.mts +0 -1
- package/src/array/impl/array-utils-transformation.mts +8 -6
- package/src/array/impl/array-utils-validation.mts +1 -5
- package/src/collections/imap-mapped.mts +696 -34
- package/src/collections/imap-mapped.test.mts +61 -13
- package/src/collections/imap.mts +20 -9
- package/src/collections/imap.test.mts +9 -8
- package/src/collections/iset-mapped.mts +18 -10
- package/src/collections/iset-mapped.test.mts +6 -1
- package/src/collections/iset.mts +11 -4
- package/src/collections/iset.test.mts +6 -8
- package/src/collections/queue.mts +32 -32
- package/src/collections/stack.mts +26 -26
- package/src/functional/optional/impl/optional-flat-map.mts +2 -2
- package/src/functional/optional.test.mts +6 -4
- package/src/functional/pipe.mts +0 -1
- package/src/functional/pipe.test.mts +2 -1
- package/src/functional/result/impl/result-flat-map.mts +2 -2
- package/src/functional/result.test.mts +3 -2
- package/src/functional/ternary-result/impl/ternary-result-flat-map.mts +5 -3
- package/src/functional/ternary-result.test.mts +8 -3
- package/src/guard/has-key.mts +1 -3
- package/src/guard/is-non-null-object.mts +0 -1
- package/src/guard/key-is-in.mts +1 -3
- package/src/json/json.mts +5 -8
- package/src/json/json.test.mts +2 -2
- package/src/number/branded-types/non-zero-safe-int.mts +2 -2
- package/src/number/branded-types/positive-safe-int.mts +1 -1
- package/src/number/branded-types/safe-int.mts +2 -2
- package/src/number/branded-types/safe-uint.mts +1 -1
- package/src/number/num-arithmetic-types.mts +0 -4
- package/src/number/num.mts +3 -3
- package/src/number/num.test.mts +2 -2
- package/src/object/object.mts +2 -2
- package/src/others/fast-deep-equal.mts +0 -2
- package/src/others/map-nullable.test.mts +4 -3
- package/src/others/unknown-to-string.mts +2 -2
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type DeepReadonly } from 'ts-type-forge';
|
|
2
|
-
import { Optional } from '../functional/index.mjs';
|
|
2
|
+
import { Optional, Result } from '../functional/index.mjs';
|
|
3
|
+
import { Num } from '../number/index.mjs';
|
|
3
4
|
import { IMapMapped } from './imap-mapped.mjs';
|
|
4
5
|
|
|
5
6
|
const toKey = (a: Readonly<{ v: number }>): number => a.v;
|
|
@@ -14,7 +15,10 @@ const testKeyToString = (key: Readonly<TestKey>): string =>
|
|
|
14
15
|
const stringToTestKey = (str: string): TestKey => {
|
|
15
16
|
const [type, idStr] = str.split('_');
|
|
16
17
|
|
|
17
|
-
return {
|
|
18
|
+
return {
|
|
19
|
+
type: type ?? '',
|
|
20
|
+
id: Result.unwrapOkOr(Num.safeParseFloat(idStr ?? '0'), Number.NaN),
|
|
21
|
+
};
|
|
18
22
|
};
|
|
19
23
|
|
|
20
24
|
describe('IMapMapped[Symbol.iterator]', () => {
|
|
@@ -112,7 +116,9 @@ describe('IMapMapped.create', () => {
|
|
|
112
116
|
const [idStr, arrStr] = str.split('_');
|
|
113
117
|
|
|
114
118
|
return {
|
|
115
|
-
nested: {
|
|
119
|
+
nested: {
|
|
120
|
+
id: Result.unwrapOkOr(Num.safeParseFloat(idStr ?? '0'), Number.NaN),
|
|
121
|
+
},
|
|
116
122
|
arr: (arrStr ?? '').split(',').map(Number),
|
|
117
123
|
};
|
|
118
124
|
};
|
|
@@ -222,6 +228,31 @@ describe('IMapMapped.equal', () => {
|
|
|
222
228
|
|
|
223
229
|
assert.isTrue(IMapMapped.equal(map1, map2));
|
|
224
230
|
});
|
|
231
|
+
|
|
232
|
+
test('should return true for non-empty maps with identical entries', () => {
|
|
233
|
+
// Regression: the old implementation compared `b.get(k) === v` where
|
|
234
|
+
// `b.get(k)` returns `Optional<V>` and `v` is `V`, so the comparison was
|
|
235
|
+
// always false and `equal` never returned true for non-empty maps.
|
|
236
|
+
const map1 = IMapMapped.create(
|
|
237
|
+
[
|
|
238
|
+
[{ id: 1, type: 'user' }, 'Alice'],
|
|
239
|
+
[{ id: 2, type: 'admin' }, 'Bob'],
|
|
240
|
+
],
|
|
241
|
+
testKeyToString,
|
|
242
|
+
stringToTestKey,
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
const map2 = IMapMapped.create(
|
|
246
|
+
[
|
|
247
|
+
[{ id: 2, type: 'admin' }, 'Bob'],
|
|
248
|
+
[{ id: 1, type: 'user' }, 'Alice'],
|
|
249
|
+
],
|
|
250
|
+
testKeyToString,
|
|
251
|
+
stringToTestKey,
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
assert.isTrue(IMapMapped.equal(map1, map2));
|
|
255
|
+
});
|
|
225
256
|
});
|
|
226
257
|
|
|
227
258
|
describe('IMapMapped.size', () => {
|
|
@@ -398,6 +429,26 @@ describe('IMapMapped.set', () => {
|
|
|
398
429
|
);
|
|
399
430
|
});
|
|
400
431
|
|
|
432
|
+
test('should return same instance when setting an existing key with the same value', () => {
|
|
433
|
+
// Regression: the old implementation compared `value === this.get(key)`
|
|
434
|
+
// where `this.get(key)` returns `Optional<V>` and `value` is `V`, so the
|
|
435
|
+
// comparison was always false and the early return never fired — a new
|
|
436
|
+
// instance was always created even when the value was unchanged.
|
|
437
|
+
const s0 = IMapMapped.create(
|
|
438
|
+
[
|
|
439
|
+
[{ v: 1 }, '1'],
|
|
440
|
+
[{ v: 2 }, '2'],
|
|
441
|
+
[{ v: 3 }, '3'],
|
|
442
|
+
],
|
|
443
|
+
toKey,
|
|
444
|
+
fromKey,
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
const result = s0.set({ v: 3 }, '3');
|
|
448
|
+
|
|
449
|
+
assert.isTrue(result === s0);
|
|
450
|
+
});
|
|
451
|
+
|
|
401
452
|
test('case 3', () => {
|
|
402
453
|
const s0 = IMapMapped.create<Readonly<{ v: number }>, string, number>(
|
|
403
454
|
[],
|
|
@@ -602,7 +653,6 @@ describe('IMapMapped.some', () => {
|
|
|
602
653
|
stringToTestKey,
|
|
603
654
|
);
|
|
604
655
|
|
|
605
|
-
// eslint-disable-next-line unicorn/prefer-includes
|
|
606
656
|
assert.isTrue(map.some((value) => value === 'Alice'));
|
|
607
657
|
});
|
|
608
658
|
|
|
@@ -616,7 +666,6 @@ describe('IMapMapped.some', () => {
|
|
|
616
666
|
stringToTestKey,
|
|
617
667
|
);
|
|
618
668
|
|
|
619
|
-
// eslint-disable-next-line unicorn/prefer-includes
|
|
620
669
|
assert.isFalse(map.some((value) => value === 'Charlie'));
|
|
621
670
|
});
|
|
622
671
|
|
|
@@ -799,17 +848,16 @@ describe('IMapMapped.forEach', () => {
|
|
|
799
848
|
stringToTestKey,
|
|
800
849
|
);
|
|
801
850
|
|
|
802
|
-
const
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
}
|
|
851
|
+
const collected: DeepReadonly<[TestKey, string][]> = Array.from(
|
|
852
|
+
map.entries(),
|
|
853
|
+
([key, value]) => [key, value],
|
|
854
|
+
);
|
|
807
855
|
|
|
808
|
-
expect(
|
|
856
|
+
expect(collected).toHaveLength(2);
|
|
809
857
|
|
|
810
|
-
expect(
|
|
858
|
+
expect(collected).toContainEqual([{ id: 1, type: 'user' }, 'Alice']);
|
|
811
859
|
|
|
812
|
-
expect(
|
|
860
|
+
expect(collected).toContainEqual([{ id: 2, type: 'admin' }, 'Bob']);
|
|
813
861
|
});
|
|
814
862
|
});
|
|
815
863
|
|
package/src/collections/imap.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable unicorn/prefer-iterator-to-array-at-end */
|
|
1
2
|
import { type WidenLiteral } from 'ts-type-forge';
|
|
2
3
|
import { Optional, pipe } from '../functional/index.mjs';
|
|
3
4
|
import { asUint32 } from '../number/index.mjs';
|
|
@@ -87,7 +88,7 @@ type IMapInterface<K extends MapSetKeyType, V> = Readonly<{
|
|
|
87
88
|
* ```
|
|
88
89
|
*
|
|
89
90
|
* @param key The key to retrieve.
|
|
90
|
-
* @returns The value associated with the key wrapped with Optional.some
|
|
91
|
+
* @returns The value associated with the key wrapped with `Optional.some`, or
|
|
91
92
|
* `Optional.none` if the key does not exist.
|
|
92
93
|
*/
|
|
93
94
|
get: (key: K | (WidenLiteral<K> & {})) => Optional<V>;
|
|
@@ -390,9 +391,9 @@ type IMapInterface<K extends MapSetKeyType, V> = Readonly<{
|
|
|
390
391
|
*
|
|
391
392
|
* const mut_entries: (readonly [string, number])[] = [];
|
|
392
393
|
*
|
|
393
|
-
*
|
|
394
|
+
* map.forEach((value, key) => {
|
|
394
395
|
* mut_entries.push([key, value]);
|
|
395
|
-
* }
|
|
396
|
+
* });
|
|
396
397
|
*
|
|
397
398
|
* assert.deepStrictEqual(mut_entries, [
|
|
398
399
|
* ['a', 1],
|
|
@@ -815,11 +816,11 @@ class IMapClass<K extends MapSetKeyType, V>
|
|
|
815
816
|
|
|
816
817
|
if (!this.has(key)) {
|
|
817
818
|
return IMap.create([...this.#map, tp(key, value)]);
|
|
818
|
-
} else {
|
|
819
|
-
return IMap.create(
|
|
820
|
-
Array.from(this.#map, ([k, v]) => tp(k, Object.is(k, key) ? value : v)),
|
|
821
|
-
);
|
|
822
819
|
}
|
|
820
|
+
|
|
821
|
+
return IMap.create(
|
|
822
|
+
Array.from(this.#map, ([k, v]) => tp(k, Object.is(k, key) ? value : v)),
|
|
823
|
+
);
|
|
823
824
|
}
|
|
824
825
|
|
|
825
826
|
/** @inheritdoc */
|
|
@@ -853,7 +854,13 @@ class IMapClass<K extends MapSetKeyType, V>
|
|
|
853
854
|
): IMap<K, V> {
|
|
854
855
|
const mut_result = new Map<K, V>(this.#map);
|
|
855
856
|
|
|
856
|
-
|
|
857
|
+
const updateFn = (
|
|
858
|
+
action: Readonly<
|
|
859
|
+
| { type: 'delete'; key: K }
|
|
860
|
+
| { type: 'set'; key: K; value: V }
|
|
861
|
+
| { type: 'update'; key: K; updater: (value: V) => V }
|
|
862
|
+
>,
|
|
863
|
+
): void => {
|
|
857
864
|
switch (action.type) {
|
|
858
865
|
case 'delete':
|
|
859
866
|
mut_result.delete(action.key);
|
|
@@ -885,6 +892,10 @@ class IMapClass<K extends MapSetKeyType, V>
|
|
|
885
892
|
break;
|
|
886
893
|
}
|
|
887
894
|
}
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
for (const action of actions) {
|
|
898
|
+
updateFn(action);
|
|
888
899
|
}
|
|
889
900
|
|
|
890
901
|
return IMap.create(mut_result);
|
|
@@ -909,7 +920,7 @@ class IMapClass<K extends MapSetKeyType, V>
|
|
|
909
920
|
|
|
910
921
|
/** @inheritdoc */
|
|
911
922
|
forEach(callbackfn: (value: V, key: K) => void): void {
|
|
912
|
-
for (const [key, value] of this.#map
|
|
923
|
+
for (const [key, value] of this.#map) {
|
|
913
924
|
callbackfn(value, key);
|
|
914
925
|
}
|
|
915
926
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Optional } from '../functional/index.mjs';
|
|
1
|
+
import { Optional, Result } from '../functional/index.mjs';
|
|
2
2
|
import { isString } from '../guard/index.mjs';
|
|
3
|
+
import { Num } from '../number/index.mjs';
|
|
3
4
|
import { IMap } from './imap.mjs';
|
|
4
5
|
|
|
5
6
|
describe('IMap[Symbol.iterator]', () => {
|
|
@@ -829,7 +830,9 @@ describe('IMap.mapKeys', () => {
|
|
|
829
830
|
['2', 'two'],
|
|
830
831
|
]);
|
|
831
832
|
|
|
832
|
-
const mapped = map.mapKeys((key) =>
|
|
833
|
+
const mapped = map.mapKeys((key) =>
|
|
834
|
+
Result.unwrapOkOr(Num.safeParseInt(key), Number.NaN),
|
|
835
|
+
);
|
|
833
836
|
|
|
834
837
|
expect(Optional.unwrap(mapped.get(1))).toBe('one');
|
|
835
838
|
|
|
@@ -896,11 +899,10 @@ describe('IMap.forEach', () => {
|
|
|
896
899
|
['c', 3],
|
|
897
900
|
]);
|
|
898
901
|
|
|
899
|
-
const mut_collected: [string, number][] =
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
}
|
|
902
|
+
const mut_collected: [string, number][] = Array.from(
|
|
903
|
+
map.entries(),
|
|
904
|
+
([key, value]) => [key, value],
|
|
905
|
+
);
|
|
904
906
|
|
|
905
907
|
expect(mut_collected).toHaveLength(3);
|
|
906
908
|
|
|
@@ -916,7 +918,6 @@ describe('IMap.forEach', () => {
|
|
|
916
918
|
|
|
917
919
|
let mut_called = false;
|
|
918
920
|
|
|
919
|
-
// eslint-disable-next-line unicorn/no-array-for-each
|
|
920
921
|
map.forEach(() => {
|
|
921
922
|
mut_called = true;
|
|
922
923
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable unicorn/prefer-iterator-to-array-at-end */
|
|
2
|
+
import { type ReadonlyRecord, type WidenLiteral } from 'ts-type-forge';
|
|
2
3
|
import { asUint32 } from '../number/index.mjs';
|
|
3
4
|
import { type MapSetKeyType, type SizeType } from '../types.mjs';
|
|
4
5
|
|
|
@@ -126,7 +127,7 @@ type ISetMappedInterface<K, KM extends MapSetKeyType> = Readonly<{
|
|
|
126
127
|
* @param key The element to check.
|
|
127
128
|
* @returns `true` if the element exists, `false` otherwise.
|
|
128
129
|
*/
|
|
129
|
-
has: (key: K) => boolean;
|
|
130
|
+
has: (key: K | (WidenLiteral<K> & {})) => boolean;
|
|
130
131
|
|
|
131
132
|
// Reducing a value
|
|
132
133
|
|
|
@@ -471,9 +472,9 @@ type ISetMappedInterface<K, KM extends MapSetKeyType> = Readonly<{
|
|
|
471
472
|
*
|
|
472
473
|
* const mut_collected: Point[] = [];
|
|
473
474
|
*
|
|
474
|
-
*
|
|
475
|
+
* set.forEach((point) => {
|
|
475
476
|
* mut_collected.push(point);
|
|
476
|
-
* }
|
|
477
|
+
* });
|
|
477
478
|
*
|
|
478
479
|
* assert.deepStrictEqual(mut_collected, [
|
|
479
480
|
* { x: 1, tag: 'a' },
|
|
@@ -1218,8 +1219,9 @@ class ISetMappedClass<K, KM extends MapSetKeyType>
|
|
|
1218
1219
|
}
|
|
1219
1220
|
|
|
1220
1221
|
/** @inheritdoc */
|
|
1221
|
-
has(key: K): boolean {
|
|
1222
|
-
|
|
1222
|
+
has(key: K | (WidenLiteral<K> & {})): boolean {
|
|
1223
|
+
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
|
|
1224
|
+
return this.#set.has(this.#toKey(key as K));
|
|
1223
1225
|
}
|
|
1224
1226
|
|
|
1225
1227
|
/** @inheritdoc */
|
|
@@ -1290,7 +1292,9 @@ class ISetMappedClass<K, KM extends MapSetKeyType>
|
|
|
1290
1292
|
): ISetMapped<K, KM> {
|
|
1291
1293
|
const mut_result = new Set<KM>(this.#set);
|
|
1292
1294
|
|
|
1293
|
-
|
|
1295
|
+
const updateFn = (
|
|
1296
|
+
action: Readonly<{ type: 'add'; key: K } | { type: 'delete'; key: K }>,
|
|
1297
|
+
): void => {
|
|
1294
1298
|
const key = this.#toKey(action.key);
|
|
1295
1299
|
|
|
1296
1300
|
switch (action.type) {
|
|
@@ -1304,6 +1308,10 @@ class ISetMappedClass<K, KM extends MapSetKeyType>
|
|
|
1304
1308
|
|
|
1305
1309
|
break;
|
|
1306
1310
|
}
|
|
1311
|
+
};
|
|
1312
|
+
|
|
1313
|
+
for (const action of actions) {
|
|
1314
|
+
updateFn(action);
|
|
1307
1315
|
}
|
|
1308
1316
|
|
|
1309
1317
|
return ISetMapped.create<K, KM>(
|
|
@@ -1422,14 +1430,14 @@ class ISetMappedClass<K, KM extends MapSetKeyType>
|
|
|
1422
1430
|
|
|
1423
1431
|
/** @inheritdoc */
|
|
1424
1432
|
*keys(): IterableIterator<K> {
|
|
1425
|
-
for (const km of this.#set
|
|
1433
|
+
for (const km of this.#set) {
|
|
1426
1434
|
yield this.#fromKey(km);
|
|
1427
1435
|
}
|
|
1428
1436
|
}
|
|
1429
1437
|
|
|
1430
1438
|
/** @inheritdoc */
|
|
1431
1439
|
*values(): IterableIterator<K> {
|
|
1432
|
-
for (const km of this.#set
|
|
1440
|
+
for (const km of this.#set) {
|
|
1433
1441
|
// JavaScript Set's values() is an alias for keys()
|
|
1434
1442
|
yield this.#fromKey(km);
|
|
1435
1443
|
}
|
|
@@ -1437,7 +1445,7 @@ class ISetMappedClass<K, KM extends MapSetKeyType>
|
|
|
1437
1445
|
|
|
1438
1446
|
/** @inheritdoc */
|
|
1439
1447
|
*entries(): IterableIterator<readonly [K, K]> {
|
|
1440
|
-
for (const km of this.#set
|
|
1448
|
+
for (const km of this.#set) {
|
|
1441
1449
|
// JavaScript Set's entries() yields [value, value]
|
|
1442
1450
|
const a = this.#fromKey(km);
|
|
1443
1451
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Result } from '../functional/index.mjs';
|
|
2
|
+
import { Num } from '../number/index.mjs';
|
|
1
3
|
import { ISetMapped } from './iset-mapped.mjs';
|
|
2
4
|
|
|
3
5
|
const toKey = (a: Readonly<{ v: number }>): number => a.v;
|
|
@@ -13,7 +15,10 @@ const testElementToString = (el: Readonly<TestElement>): string =>
|
|
|
13
15
|
const stringToTestElement = (str: string): TestElement => {
|
|
14
16
|
const [type, idStr] = str.split('_');
|
|
15
17
|
|
|
16
|
-
return {
|
|
18
|
+
return {
|
|
19
|
+
type: type ?? '',
|
|
20
|
+
id: Result.unwrapOkOr(Num.safeParseFloat(idStr ?? '0'), Number.NaN),
|
|
21
|
+
};
|
|
17
22
|
};
|
|
18
23
|
|
|
19
24
|
describe('ISetMapped[Symbol.iterator]', () => {
|
package/src/collections/iset.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable unicorn/prefer-iterator-to-array-at-end */
|
|
1
2
|
import { type ReadonlyRecord, type WidenLiteral } from 'ts-type-forge';
|
|
2
3
|
import { asUint32 } from '../number/index.mjs';
|
|
3
4
|
import { unknownToString } from '../others/index.mjs';
|
|
@@ -394,9 +395,9 @@ type ISetInterface<K extends MapSetKeyType> = Readonly<{
|
|
|
394
395
|
*
|
|
395
396
|
* const mut_collected: string[] = [];
|
|
396
397
|
*
|
|
397
|
-
*
|
|
398
|
+
* set.forEach((value) => {
|
|
398
399
|
* mut_collected.push(value);
|
|
399
|
-
* }
|
|
400
|
+
* });
|
|
400
401
|
*
|
|
401
402
|
* assert.deepStrictEqual(mut_collected, ['alpha', 'beta']);
|
|
402
403
|
* ```
|
|
@@ -806,7 +807,9 @@ class ISetClass<K extends MapSetKeyType> implements ISet<K>, Iterable<K> {
|
|
|
806
807
|
): ISet<K> {
|
|
807
808
|
const mut_result = new Set<K>(this.#set);
|
|
808
809
|
|
|
809
|
-
|
|
810
|
+
const updateFn = (
|
|
811
|
+
action: Readonly<{ type: 'add'; key: K } | { type: 'delete'; key: K }>,
|
|
812
|
+
): void => {
|
|
810
813
|
switch (action.type) {
|
|
811
814
|
case 'delete':
|
|
812
815
|
mut_result.delete(action.key);
|
|
@@ -818,6 +821,10 @@ class ISetClass<K extends MapSetKeyType> implements ISet<K>, Iterable<K> {
|
|
|
818
821
|
|
|
819
822
|
break;
|
|
820
823
|
}
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
for (const action of actions) {
|
|
827
|
+
updateFn(action);
|
|
821
828
|
}
|
|
822
829
|
|
|
823
830
|
return ISet.create(mut_result);
|
|
@@ -846,7 +853,7 @@ class ISetClass<K extends MapSetKeyType> implements ISet<K>, Iterable<K> {
|
|
|
846
853
|
|
|
847
854
|
/** @inheritdoc */
|
|
848
855
|
forEach(callbackfn: (key: K) => void): void {
|
|
849
|
-
for (const v of this.#set
|
|
856
|
+
for (const v of this.#set) {
|
|
850
857
|
callbackfn(v);
|
|
851
858
|
}
|
|
852
859
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Primitive } from 'ts-type-forge';
|
|
1
|
+
import { type DeepReadonly, type Primitive } from 'ts-type-forge';
|
|
2
2
|
import { expectType } from '../expect-type.mjs';
|
|
3
3
|
import { isString } from '../guard/index.mjs';
|
|
4
4
|
import { type MapSetKeyType } from '../types.mjs';
|
|
@@ -922,7 +922,6 @@ describe('ISet.forEach', () => {
|
|
|
922
922
|
|
|
923
923
|
let mut_callCount = 0;
|
|
924
924
|
|
|
925
|
-
// eslint-disable-next-line unicorn/no-array-for-each
|
|
926
925
|
set.forEach(() => {
|
|
927
926
|
mut_callCount += 1;
|
|
928
927
|
});
|
|
@@ -978,14 +977,13 @@ describe('ISet.entries', () => {
|
|
|
978
977
|
() => IterableIterator<readonly [1 | 2 | 3, 1 | 2 | 3]>
|
|
979
978
|
>('<=');
|
|
980
979
|
|
|
981
|
-
const
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
}
|
|
980
|
+
const result: DeepReadonly<[1 | 2 | 3, 1 | 2 | 3][]> = Array.from(
|
|
981
|
+
s0.entries(),
|
|
982
|
+
(x) => [x[0], x[1]],
|
|
983
|
+
);
|
|
986
984
|
|
|
987
985
|
assert.deepStrictEqual(
|
|
988
|
-
|
|
986
|
+
result.toSorted((a, b) => a[0] - b[0]),
|
|
989
987
|
[
|
|
990
988
|
[1, 1],
|
|
991
989
|
[2, 2],
|
|
@@ -205,6 +205,9 @@ export type Queue<T> = Readonly<{
|
|
|
205
205
|
* @implements Queue
|
|
206
206
|
*/
|
|
207
207
|
class QueueClass<T> implements Queue<T> {
|
|
208
|
+
/** @internal Initial capacity for new queues. */
|
|
209
|
+
static readonly #INITIAL_CAPACITY = 8;
|
|
210
|
+
|
|
208
211
|
/** @internal Circular buffer to store queue elements. */
|
|
209
212
|
#mut_buffer: (T | undefined)[];
|
|
210
213
|
|
|
@@ -220,9 +223,6 @@ class QueueClass<T> implements Queue<T> {
|
|
|
220
223
|
/** @internal Current capacity of the buffer. */
|
|
221
224
|
#mut_capacity: number;
|
|
222
225
|
|
|
223
|
-
/** @internal Initial capacity for new queues. */
|
|
224
|
-
static readonly #INITIAL_CAPACITY = 8;
|
|
225
|
-
|
|
226
226
|
/**
|
|
227
227
|
* Constructs a new QueueClass instance.
|
|
228
228
|
*
|
|
@@ -252,6 +252,35 @@ class QueueClass<T> implements Queue<T> {
|
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
+
/**
|
|
256
|
+
* @internal
|
|
257
|
+
* Resizes the circular buffer when it becomes full.
|
|
258
|
+
* Doubles the capacity and reorganizes elements to maintain queue order.
|
|
259
|
+
*/
|
|
260
|
+
#resize(): void {
|
|
261
|
+
const newCapacity = asUint32(this.#mut_capacity * 2);
|
|
262
|
+
|
|
263
|
+
const newBuffer = Array.from<unknown, T | undefined>(
|
|
264
|
+
{ length: newCapacity },
|
|
265
|
+
() => undefined,
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
// Copy elements in order from head to tail
|
|
269
|
+
for (const i of range(asSafeUint(this.#mut_size))) {
|
|
270
|
+
const sourceIndex = (this.#mut_head + i) % this.#mut_capacity;
|
|
271
|
+
|
|
272
|
+
newBuffer[i] = this.#mut_buffer[sourceIndex];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
this.#mut_buffer = newBuffer;
|
|
276
|
+
|
|
277
|
+
this.#mut_head = 0;
|
|
278
|
+
|
|
279
|
+
this.#mut_tail = this.#mut_size;
|
|
280
|
+
|
|
281
|
+
this.#mut_capacity = newCapacity;
|
|
282
|
+
}
|
|
283
|
+
|
|
255
284
|
/** @inheritdoc */
|
|
256
285
|
get isEmpty(): boolean {
|
|
257
286
|
return this.#mut_size === 0;
|
|
@@ -322,35 +351,6 @@ class QueueClass<T> implements Queue<T> {
|
|
|
322
351
|
|
|
323
352
|
this.#mut_size += 1;
|
|
324
353
|
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* @internal
|
|
328
|
-
* Resizes the circular buffer when it becomes full.
|
|
329
|
-
* Doubles the capacity and reorganizes elements to maintain queue order.
|
|
330
|
-
*/
|
|
331
|
-
#resize(): void {
|
|
332
|
-
const newCapacity = asUint32(this.#mut_capacity * 2);
|
|
333
|
-
|
|
334
|
-
const newBuffer = Array.from<unknown, T | undefined>(
|
|
335
|
-
{ length: newCapacity },
|
|
336
|
-
() => undefined,
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
// Copy elements in order from head to tail
|
|
340
|
-
for (const i of range(asSafeUint(this.#mut_size))) {
|
|
341
|
-
const sourceIndex = (this.#mut_head + i) % this.#mut_capacity;
|
|
342
|
-
|
|
343
|
-
newBuffer[i] = this.#mut_buffer[sourceIndex];
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
this.#mut_buffer = newBuffer;
|
|
347
|
-
|
|
348
|
-
this.#mut_head = 0;
|
|
349
|
-
|
|
350
|
-
this.#mut_tail = this.#mut_size;
|
|
351
|
-
|
|
352
|
-
this.#mut_capacity = newCapacity;
|
|
353
|
-
}
|
|
354
354
|
}
|
|
355
355
|
|
|
356
356
|
/**
|
|
@@ -200,6 +200,9 @@ export type Stack<T> = Readonly<{
|
|
|
200
200
|
* @implements Stack
|
|
201
201
|
*/
|
|
202
202
|
class StackClass<T> implements Stack<T> {
|
|
203
|
+
/** @internal Initial capacity for new stacks. */
|
|
204
|
+
static readonly #INITIAL_CAPACITY = 8;
|
|
205
|
+
|
|
203
206
|
/** @internal Dynamic array to store stack elements. */
|
|
204
207
|
#mut_buffer: (T | undefined)[];
|
|
205
208
|
|
|
@@ -209,9 +212,6 @@ class StackClass<T> implements Stack<T> {
|
|
|
209
212
|
/** @internal Current capacity of the buffer. */
|
|
210
213
|
#mut_capacity: Uint32;
|
|
211
214
|
|
|
212
|
-
/** @internal Initial capacity for new stacks. */
|
|
213
|
-
static readonly #INITIAL_CAPACITY = 8;
|
|
214
|
-
|
|
215
215
|
/**
|
|
216
216
|
* Constructs a new StackClass instance.
|
|
217
217
|
*
|
|
@@ -237,6 +237,29 @@ class StackClass<T> implements Stack<T> {
|
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
+
/**
|
|
241
|
+
* @internal
|
|
242
|
+
* Resizes the buffer when it becomes full.
|
|
243
|
+
* Doubles the capacity while preserving all elements.
|
|
244
|
+
*/
|
|
245
|
+
#resize(): void {
|
|
246
|
+
const newCapacity = asUint32(this.#mut_capacity * 2);
|
|
247
|
+
|
|
248
|
+
const newBuffer = Array.from<unknown, T | undefined>(
|
|
249
|
+
{ length: newCapacity },
|
|
250
|
+
() => undefined,
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
// Copy existing elements
|
|
254
|
+
for (const i of range(this.#mut_size)) {
|
|
255
|
+
newBuffer[i] = this.#mut_buffer[i];
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
this.#mut_buffer = newBuffer;
|
|
259
|
+
|
|
260
|
+
this.#mut_capacity = newCapacity;
|
|
261
|
+
}
|
|
262
|
+
|
|
240
263
|
/** @inheritdoc */
|
|
241
264
|
get isEmpty(): boolean {
|
|
242
265
|
return this.#mut_size === 0;
|
|
@@ -303,29 +326,6 @@ class StackClass<T> implements Stack<T> {
|
|
|
303
326
|
|
|
304
327
|
this.#mut_size = Uint32.add(this.#mut_size, 1);
|
|
305
328
|
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* @internal
|
|
309
|
-
* Resizes the buffer when it becomes full.
|
|
310
|
-
* Doubles the capacity while preserving all elements.
|
|
311
|
-
*/
|
|
312
|
-
#resize(): void {
|
|
313
|
-
const newCapacity = asUint32(this.#mut_capacity * 2);
|
|
314
|
-
|
|
315
|
-
const newBuffer = Array.from<unknown, T | undefined>(
|
|
316
|
-
{ length: newCapacity },
|
|
317
|
-
() => undefined,
|
|
318
|
-
);
|
|
319
|
-
|
|
320
|
-
// Copy existing elements
|
|
321
|
-
for (const i of range(this.#mut_size)) {
|
|
322
|
-
newBuffer[i] = this.#mut_buffer[i];
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
this.#mut_buffer = newBuffer;
|
|
326
|
-
|
|
327
|
-
this.#mut_capacity = newCapacity;
|
|
328
|
-
}
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
/**
|
|
@@ -13,9 +13,9 @@ import { type Unwrap } from './types.mjs';
|
|
|
13
13
|
*
|
|
14
14
|
* ```ts
|
|
15
15
|
* const parseNumber = (input: string): Optional<number> => {
|
|
16
|
-
* const num =
|
|
16
|
+
* const num = Num.safeParseInt(input);
|
|
17
17
|
*
|
|
18
|
-
* return
|
|
18
|
+
* return Result.isErr(num) ? Optional.none : Optional.some(num.value);
|
|
19
19
|
* };
|
|
20
20
|
*
|
|
21
21
|
* const parsed = Optional.flatMap(Optional.some('10'), parseNumber);
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { expectType } from '../expect-type.mjs';
|
|
2
|
+
import { Num } from '../number/index.mjs';
|
|
2
3
|
import { type None, type Some } from '../types.mjs';
|
|
3
4
|
import { Optional } from './optional/index.mjs';
|
|
4
5
|
import { pipe } from './pipe.mjs';
|
|
6
|
+
import { Result } from './result/index.mjs';
|
|
5
7
|
|
|
6
8
|
describe('Optional test', () => {
|
|
7
9
|
describe('isOptional', () => {
|
|
@@ -374,7 +376,7 @@ describe('Optional test', () => {
|
|
|
374
376
|
describe('flatMap', () => {
|
|
375
377
|
test('should chain operations that return Optional', () => {
|
|
376
378
|
const parseNumber = (s: string): Optional<number> => {
|
|
377
|
-
const n =
|
|
379
|
+
const n = Result.unwrapOkOr(Num.safeParseFloat(s), Number.NaN);
|
|
378
380
|
|
|
379
381
|
return Number.isNaN(n) ? Optional.none : Optional.some(n);
|
|
380
382
|
};
|
|
@@ -400,7 +402,7 @@ describe('Optional test', () => {
|
|
|
400
402
|
|
|
401
403
|
test('should support chaining multiple flatMaps', () => {
|
|
402
404
|
const parseNumber = (s: string): Optional<number> => {
|
|
403
|
-
const n =
|
|
405
|
+
const n = Result.unwrapOkOr(Num.safeParseFloat(s), Number.NaN);
|
|
404
406
|
|
|
405
407
|
return Number.isNaN(n) ? Optional.none : Optional.some(n);
|
|
406
408
|
};
|
|
@@ -424,7 +426,7 @@ describe('Optional test', () => {
|
|
|
424
426
|
|
|
425
427
|
test('should support curried form', () => {
|
|
426
428
|
const parseNumber = (s: string): Optional<number> => {
|
|
427
|
-
const n =
|
|
429
|
+
const n = Result.unwrapOkOr(Num.safeParseFloat(s), Number.NaN);
|
|
428
430
|
|
|
429
431
|
return Number.isNaN(n) ? Optional.none : Optional.some(n);
|
|
430
432
|
};
|
|
@@ -450,7 +452,7 @@ describe('Optional test', () => {
|
|
|
450
452
|
|
|
451
453
|
test('should work with pipe when curried', () => {
|
|
452
454
|
const parseNumber = (s: string): Optional<number> => {
|
|
453
|
-
const n =
|
|
455
|
+
const n = Result.unwrapOkOr(Num.safeParseFloat(s), Number.NaN);
|
|
454
456
|
|
|
455
457
|
return Number.isNaN(n) ? Optional.none : Optional.some(n);
|
|
456
458
|
};
|
package/src/functional/pipe.mts
CHANGED
|
@@ -49,7 +49,6 @@ export const pipe = <const A,>(a: A): Pipe<A> =>
|
|
|
49
49
|
// the deferred true branch narrows `A` to `A & UnknownOptional`, so
|
|
50
50
|
// `.value` would surface as the noisy `(A & UnknownOptional) | A`. Keeping
|
|
51
51
|
// it on the unconditional base yields a clean `A`.
|
|
52
|
-
// transformer-ignore-next-line convert-to-readonly
|
|
53
52
|
export type Pipe<A> = PipeBase<A> &
|
|
54
53
|
PipeMapNullable<A> &
|
|
55
54
|
([A] extends [UnknownOptional] ? PipeMapOptional<A> : unknown);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { expectType } from '../expect-type.mjs';
|
|
2
|
+
import { Num } from '../number/index.mjs';
|
|
2
3
|
import { type None, type Some } from '../types.mjs';
|
|
3
4
|
import { Optional } from './optional/index.mjs';
|
|
4
5
|
import { pipe } from './pipe.mjs';
|
|
@@ -89,7 +90,7 @@ describe(pipe, () => {
|
|
|
89
90
|
|
|
90
91
|
test('chaining multiple operations', () => {
|
|
91
92
|
const result = pipe('5')
|
|
92
|
-
.map((s) =>
|
|
93
|
+
.map((s) => Result.unwrapOkOr(Num.safeParseInt(s), Number.NaN))
|
|
93
94
|
.map((n) => n * 3)
|
|
94
95
|
.map((n) => n.toString())
|
|
95
96
|
.map((s) => `${s} items`).value;
|