ts-data-forge 1.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 (143) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +534 -0
  3. package/package.json +101 -0
  4. package/src/array/array-utils-creation.test.mts +443 -0
  5. package/src/array/array-utils-modification.test.mts +197 -0
  6. package/src/array/array-utils-overload-type-error.test.mts +149 -0
  7. package/src/array/array-utils-reducing-value.test.mts +425 -0
  8. package/src/array/array-utils-search.test.mts +169 -0
  9. package/src/array/array-utils-set-op.test.mts +335 -0
  10. package/src/array/array-utils-slice-clamped.test.mts +113 -0
  11. package/src/array/array-utils-slicing.test.mts +316 -0
  12. package/src/array/array-utils-transformation.test.mts +790 -0
  13. package/src/array/array-utils-validation.test.mts +492 -0
  14. package/src/array/array-utils.mts +4000 -0
  15. package/src/array/array.test.mts +146 -0
  16. package/src/array/index.mts +2 -0
  17. package/src/array/tuple-utils.mts +519 -0
  18. package/src/array/tuple-utils.test.mts +518 -0
  19. package/src/collections/imap-mapped.mts +801 -0
  20. package/src/collections/imap-mapped.test.mts +860 -0
  21. package/src/collections/imap.mts +651 -0
  22. package/src/collections/imap.test.mts +932 -0
  23. package/src/collections/index.mts +6 -0
  24. package/src/collections/iset-mapped.mts +889 -0
  25. package/src/collections/iset-mapped.test.mts +1187 -0
  26. package/src/collections/iset.mts +682 -0
  27. package/src/collections/iset.test.mts +1084 -0
  28. package/src/collections/queue.mts +390 -0
  29. package/src/collections/queue.test.mts +282 -0
  30. package/src/collections/stack.mts +423 -0
  31. package/src/collections/stack.test.mts +225 -0
  32. package/src/expect-type.mts +206 -0
  33. package/src/functional/index.mts +4 -0
  34. package/src/functional/match.mts +300 -0
  35. package/src/functional/match.test.mts +177 -0
  36. package/src/functional/optional.mts +733 -0
  37. package/src/functional/optional.test.mts +619 -0
  38. package/src/functional/pipe.mts +212 -0
  39. package/src/functional/pipe.test.mts +85 -0
  40. package/src/functional/result.mts +1134 -0
  41. package/src/functional/result.test.mts +777 -0
  42. package/src/globals.d.mts +38 -0
  43. package/src/guard/has-key.mts +119 -0
  44. package/src/guard/has-key.test.mts +219 -0
  45. package/src/guard/index.mts +7 -0
  46. package/src/guard/is-non-empty-string.mts +108 -0
  47. package/src/guard/is-non-empty-string.test.mts +91 -0
  48. package/src/guard/is-non-null-object.mts +106 -0
  49. package/src/guard/is-non-null-object.test.mts +90 -0
  50. package/src/guard/is-primitive.mts +165 -0
  51. package/src/guard/is-primitive.test.mts +102 -0
  52. package/src/guard/is-record.mts +153 -0
  53. package/src/guard/is-record.test.mts +112 -0
  54. package/src/guard/is-type.mts +450 -0
  55. package/src/guard/is-type.test.mts +496 -0
  56. package/src/guard/key-is-in.mts +163 -0
  57. package/src/guard/key-is-in.test.mts +19 -0
  58. package/src/index.mts +10 -0
  59. package/src/iterator/index.mts +1 -0
  60. package/src/iterator/range.mts +120 -0
  61. package/src/iterator/range.test.mts +33 -0
  62. package/src/json/index.mts +1 -0
  63. package/src/json/json.mts +711 -0
  64. package/src/json/json.test.mts +628 -0
  65. package/src/number/branded-types/finite-number.mts +354 -0
  66. package/src/number/branded-types/finite-number.test.mts +135 -0
  67. package/src/number/branded-types/index.mts +26 -0
  68. package/src/number/branded-types/int.mts +278 -0
  69. package/src/number/branded-types/int.test.mts +140 -0
  70. package/src/number/branded-types/int16.mts +192 -0
  71. package/src/number/branded-types/int16.test.mts +170 -0
  72. package/src/number/branded-types/int32.mts +193 -0
  73. package/src/number/branded-types/int32.test.mts +170 -0
  74. package/src/number/branded-types/non-negative-finite-number.mts +223 -0
  75. package/src/number/branded-types/non-negative-finite-number.test.mts +188 -0
  76. package/src/number/branded-types/non-negative-int16.mts +187 -0
  77. package/src/number/branded-types/non-negative-int16.test.mts +201 -0
  78. package/src/number/branded-types/non-negative-int32.mts +187 -0
  79. package/src/number/branded-types/non-negative-int32.test.mts +204 -0
  80. package/src/number/branded-types/non-zero-finite-number.mts +229 -0
  81. package/src/number/branded-types/non-zero-finite-number.test.mts +198 -0
  82. package/src/number/branded-types/non-zero-int.mts +167 -0
  83. package/src/number/branded-types/non-zero-int.test.mts +177 -0
  84. package/src/number/branded-types/non-zero-int16.mts +196 -0
  85. package/src/number/branded-types/non-zero-int16.test.mts +195 -0
  86. package/src/number/branded-types/non-zero-int32.mts +196 -0
  87. package/src/number/branded-types/non-zero-int32.test.mts +197 -0
  88. package/src/number/branded-types/non-zero-safe-int.mts +196 -0
  89. package/src/number/branded-types/non-zero-safe-int.test.mts +232 -0
  90. package/src/number/branded-types/non-zero-uint16.mts +189 -0
  91. package/src/number/branded-types/non-zero-uint16.test.mts +199 -0
  92. package/src/number/branded-types/non-zero-uint32.mts +189 -0
  93. package/src/number/branded-types/non-zero-uint32.test.mts +199 -0
  94. package/src/number/branded-types/positive-finite-number.mts +241 -0
  95. package/src/number/branded-types/positive-finite-number.test.mts +204 -0
  96. package/src/number/branded-types/positive-int.mts +304 -0
  97. package/src/number/branded-types/positive-int.test.mts +176 -0
  98. package/src/number/branded-types/positive-int16.mts +188 -0
  99. package/src/number/branded-types/positive-int16.test.mts +197 -0
  100. package/src/number/branded-types/positive-int32.mts +188 -0
  101. package/src/number/branded-types/positive-int32.test.mts +197 -0
  102. package/src/number/branded-types/positive-safe-int.mts +187 -0
  103. package/src/number/branded-types/positive-safe-int.test.mts +210 -0
  104. package/src/number/branded-types/positive-uint16.mts +188 -0
  105. package/src/number/branded-types/positive-uint16.test.mts +203 -0
  106. package/src/number/branded-types/positive-uint32.mts +188 -0
  107. package/src/number/branded-types/positive-uint32.test.mts +203 -0
  108. package/src/number/branded-types/safe-int.mts +291 -0
  109. package/src/number/branded-types/safe-int.test.mts +170 -0
  110. package/src/number/branded-types/safe-uint.mts +187 -0
  111. package/src/number/branded-types/safe-uint.test.mts +176 -0
  112. package/src/number/branded-types/uint.mts +179 -0
  113. package/src/number/branded-types/uint.test.mts +158 -0
  114. package/src/number/branded-types/uint16.mts +186 -0
  115. package/src/number/branded-types/uint16.test.mts +170 -0
  116. package/src/number/branded-types/uint32.mts +218 -0
  117. package/src/number/branded-types/uint32.test.mts +170 -0
  118. package/src/number/enum/index.mts +2 -0
  119. package/src/number/enum/int8.mts +344 -0
  120. package/src/number/enum/int8.test.mts +180 -0
  121. package/src/number/enum/uint8.mts +293 -0
  122. package/src/number/enum/uint8.test.mts +164 -0
  123. package/src/number/index.mts +4 -0
  124. package/src/number/num.mts +604 -0
  125. package/src/number/num.test.mts +242 -0
  126. package/src/number/refined-number-utils.mts +566 -0
  127. package/src/object/index.mts +1 -0
  128. package/src/object/object.mts +447 -0
  129. package/src/object/object.test.mts +124 -0
  130. package/src/others/cast-mutable.mts +113 -0
  131. package/src/others/cast-readonly.mts +192 -0
  132. package/src/others/cast-readonly.test.mts +89 -0
  133. package/src/others/if-then.mts +98 -0
  134. package/src/others/if-then.test.mts +75 -0
  135. package/src/others/index.mts +7 -0
  136. package/src/others/map-nullable.mts +172 -0
  137. package/src/others/map-nullable.test.mts +297 -0
  138. package/src/others/memoize-function.mts +196 -0
  139. package/src/others/memoize-function.test.mts +168 -0
  140. package/src/others/tuple.mts +160 -0
  141. package/src/others/tuple.test.mts +11 -0
  142. package/src/others/unknown-to-string.mts +215 -0
  143. package/src/others/unknown-to-string.test.mts +114 -0
@@ -0,0 +1,860 @@
1
+ import { Optional } from '../functional/index.mjs';
2
+ import { IMapMapped } from './imap-mapped.mjs';
3
+
4
+ const toKey = (a: Readonly<{ v: number }>): number => a.v;
5
+ const fromKey = (k: number): Readonly<{ v: number }> => ({ v: k });
6
+
7
+ type TestKey = { id: number; type: string };
8
+ const testKeyToString = (key: Readonly<TestKey>): string =>
9
+ `${key.type}_${key.id}`;
10
+ const stringToTestKey = (str: string): TestKey => {
11
+ const [type, idStr] = str.split('_');
12
+ return { type: type ?? '', id: Number(idStr ?? '0') };
13
+ };
14
+
15
+ describe('IMapMapped[Symbol.iterator]', () => {
16
+ test('case 1', () => {
17
+ const s0 = IMapMapped.create(
18
+ IMapMapped.create(
19
+ [
20
+ [{ v: 1 }, '1'],
21
+ [{ v: 2 }, '2'],
22
+ [{ v: 3 }, '3'],
23
+ ],
24
+ toKey,
25
+ fromKey,
26
+ ),
27
+ toKey,
28
+ fromKey,
29
+ );
30
+
31
+ expect(s0).toStrictEqual(
32
+ IMapMapped.create(
33
+ [
34
+ [{ v: 1 }, '1'],
35
+ [{ v: 2 }, '2'],
36
+ [{ v: 3 }, '3'],
37
+ ],
38
+ toKey,
39
+ fromKey,
40
+ ),
41
+ );
42
+ });
43
+ });
44
+
45
+ describe('IMapMapped.create', () => {
46
+ test('should create empty map', () => {
47
+ const map = IMapMapped.create<TestKey, string, string>(
48
+ [],
49
+ testKeyToString,
50
+ stringToTestKey,
51
+ );
52
+ expect(map.size).toBe(0);
53
+ });
54
+
55
+ test('should create map with initial entries', () => {
56
+ const map = IMapMapped.create(
57
+ [
58
+ [{ id: 1, type: 'user' }, 'Alice'],
59
+ [{ id: 2, type: 'admin' }, 'Bob'],
60
+ ],
61
+ testKeyToString,
62
+ stringToTestKey,
63
+ );
64
+ expect(map.size).toBe(2);
65
+ expect(Optional.unwrap(map.get({ id: 1, type: 'user' }))).toBe('Alice');
66
+ expect(Optional.unwrap(map.get({ id: 2, type: 'admin' }))).toBe('Bob');
67
+ });
68
+
69
+ test('should create map from another IMapMapped', () => {
70
+ const original = IMapMapped.create(
71
+ [
72
+ [{ id: 1, type: 'user' }, 'Alice'],
73
+ [{ id: 2, type: 'admin' }, 'Bob'],
74
+ ],
75
+ testKeyToString,
76
+ stringToTestKey,
77
+ );
78
+ const copy = IMapMapped.create(original, testKeyToString, stringToTestKey);
79
+ expect(copy.size).toBe(2);
80
+ expect(Optional.unwrap(copy.get({ id: 1, type: 'user' }))).toBe('Alice');
81
+ expect(Optional.unwrap(copy.get({ id: 2, type: 'admin' }))).toBe('Bob');
82
+ });
83
+
84
+ test('should handle complex key transformations', () => {
85
+ type ComplexKey = { nested: { id: number }; arr: number[] };
86
+ const complexKeyToString = (key: {
87
+ readonly nested: { readonly id: number };
88
+ readonly arr: readonly number[];
89
+ }): string => `${key.nested.id}_${key.arr.join(',')}`;
90
+ const stringToComplexKey = (str: string): ComplexKey => {
91
+ const [idStr, arrStr] = str.split('_');
92
+ return {
93
+ nested: { id: Number(idStr ?? '0') },
94
+ arr: (arrStr ?? '').split(',').map(Number),
95
+ };
96
+ };
97
+
98
+ const map = IMapMapped.create<ComplexKey, string, string>(
99
+ [[{ nested: { id: 1 }, arr: [1, 2, 3] }, 'test']],
100
+ complexKeyToString,
101
+ stringToComplexKey,
102
+ );
103
+ expect(map.size).toBe(1);
104
+ expect(
105
+ Optional.unwrap(map.get({ nested: { id: 1 }, arr: [1, 2, 3] })),
106
+ ).toBe('test');
107
+ });
108
+ });
109
+
110
+ describe('IMapMapped.equal', () => {
111
+ test('should return true for equal maps', () => {
112
+ const map1 = IMapMapped.create(
113
+ [
114
+ [{ id: 1, type: 'user' }, 'Alice'],
115
+ [{ id: 2, type: 'admin' }, 'Bob'],
116
+ ],
117
+ testKeyToString,
118
+ stringToTestKey,
119
+ );
120
+ const map2 = IMapMapped.create(
121
+ [
122
+ [{ id: 1, type: 'user' }, 'Alice'],
123
+ [{ id: 2, type: 'admin' }, 'Bob'],
124
+ ],
125
+ testKeyToString,
126
+ stringToTestKey,
127
+ );
128
+ // Test structural equality, not reference equality
129
+ expect(map1.size).toBe(map2.size);
130
+ expect(map1.has({ id: 1, type: 'user' })).toBe(true);
131
+ expect(map2.has({ id: 1, type: 'user' })).toBe(true);
132
+ expect(Optional.unwrap(map1.get({ id: 1, type: 'user' }))).toBe(
133
+ Optional.unwrap(map2.get({ id: 1, type: 'user' })),
134
+ );
135
+ });
136
+
137
+ test('should return false for maps with different values', () => {
138
+ const map1 = IMapMapped.create(
139
+ [
140
+ [{ id: 1, type: 'user' }, 'Alice'],
141
+ [{ id: 2, type: 'admin' }, 'Bob'],
142
+ ],
143
+ testKeyToString,
144
+ stringToTestKey,
145
+ );
146
+ const map2 = IMapMapped.create(
147
+ [
148
+ [{ id: 1, type: 'user' }, 'Alice'],
149
+ [{ id: 2, type: 'admin' }, 'Charlie'],
150
+ ],
151
+ testKeyToString,
152
+ stringToTestKey,
153
+ );
154
+ expect(IMapMapped.equal(map1, map2)).toBe(false);
155
+ });
156
+
157
+ test('should return false for maps with different keys', () => {
158
+ const map1 = IMapMapped.create(
159
+ [
160
+ [{ id: 1, type: 'user' }, 'Alice'],
161
+ [{ id: 2, type: 'admin' }, 'Bob'],
162
+ ],
163
+ testKeyToString,
164
+ stringToTestKey,
165
+ );
166
+ const map2 = IMapMapped.create(
167
+ [
168
+ [{ id: 1, type: 'user' }, 'Alice'],
169
+ [{ id: 3, type: 'guest' }, 'Bob'],
170
+ ],
171
+ testKeyToString,
172
+ stringToTestKey,
173
+ );
174
+ expect(IMapMapped.equal(map1, map2)).toBe(false);
175
+ });
176
+
177
+ test('should return true for empty maps', () => {
178
+ const map1 = IMapMapped.create<TestKey, string, string>(
179
+ [],
180
+ testKeyToString,
181
+ stringToTestKey,
182
+ );
183
+ const map2 = IMapMapped.create<TestKey, string, string>(
184
+ [],
185
+ testKeyToString,
186
+ stringToTestKey,
187
+ );
188
+ expect(IMapMapped.equal(map1, map2)).toBe(true);
189
+ });
190
+ });
191
+
192
+ describe('IMapMapped.size', () => {
193
+ test('case 1', () => {
194
+ const s0 = IMapMapped.create(
195
+ [
196
+ [{ v: 1 }, '1'],
197
+ [{ v: 2 }, '2'],
198
+ [{ v: 3 }, '3'],
199
+ ],
200
+ toKey,
201
+ fromKey,
202
+ );
203
+
204
+ expect(s0.size).toBe(3);
205
+ });
206
+ });
207
+
208
+ describe('IMapMapped.has', () => {
209
+ test('case 1', () => {
210
+ const s0 = IMapMapped.create(
211
+ [
212
+ [{ v: 1 }, '1'],
213
+ [{ v: 2 }, '2'],
214
+ [{ v: 3 }, '3'],
215
+ ],
216
+ toKey,
217
+ fromKey,
218
+ );
219
+
220
+ expect(s0.has({ v: 3 })).toBe(true);
221
+ });
222
+ test('case 2', () => {
223
+ const s0 = IMapMapped.create(
224
+ [
225
+ [{ v: 1 }, '1'],
226
+ [{ v: 2 }, '2'],
227
+ [{ v: 3 }, '3'],
228
+ ],
229
+ toKey,
230
+ fromKey,
231
+ );
232
+
233
+ expect(s0.has({ v: 4 })).toBe(false);
234
+ });
235
+ test('case 3', () => {
236
+ const s0 = IMapMapped.create<Readonly<{ v: number }>, string, number>(
237
+ [],
238
+ toKey,
239
+ fromKey,
240
+ );
241
+
242
+ expect(s0.has({ v: 3 })).toBe(false);
243
+ });
244
+ });
245
+
246
+ describe('IMapMapped.get', () => {
247
+ test('case 1', () => {
248
+ const s0 = IMapMapped.create(
249
+ [
250
+ [{ v: 1 }, '1'],
251
+ [{ v: 2 }, '2'],
252
+ [{ v: 3 }, '3'],
253
+ ],
254
+ toKey,
255
+ fromKey,
256
+ );
257
+
258
+ expect(s0.get({ v: 3 })).toStrictEqual(Optional.some('3'));
259
+ });
260
+ test('case 2', () => {
261
+ const s0 = IMapMapped.create(
262
+ [
263
+ [{ v: 1 }, '1'],
264
+ [{ v: 2 }, '2'],
265
+ [{ v: 3 }, '3'],
266
+ ],
267
+ toKey,
268
+ fromKey,
269
+ );
270
+
271
+ expect(s0.get({ v: 4 })).toStrictEqual(Optional.none);
272
+ });
273
+ test('case 3', () => {
274
+ const s0 = IMapMapped.create<Readonly<{ v: number }>, string, number>(
275
+ [],
276
+ toKey,
277
+ fromKey,
278
+ );
279
+
280
+ expect(s0.get({ v: 3 })).toStrictEqual(Optional.none);
281
+ });
282
+ });
283
+
284
+ describe('IMapMapped.set', () => {
285
+ test('case 1', () => {
286
+ const s0 = IMapMapped.create(
287
+ [
288
+ [{ v: 1 }, '1'],
289
+ [{ v: 2 }, '2'],
290
+ [{ v: 3 }, '3'],
291
+ ],
292
+ toKey,
293
+ fromKey,
294
+ );
295
+
296
+ expect(s0.set({ v: 5 }, '5')).toStrictEqual(
297
+ IMapMapped.create(
298
+ [
299
+ [{ v: 1 }, '1'],
300
+ [{ v: 2 }, '2'],
301
+ [{ v: 3 }, '3'],
302
+ [{ v: 5 }, '5'],
303
+ ],
304
+ toKey,
305
+ fromKey,
306
+ ),
307
+ );
308
+ expect(s0).toStrictEqual(
309
+ IMapMapped.create(
310
+ [
311
+ [{ v: 1 }, '1'],
312
+ [{ v: 2 }, '2'],
313
+ [{ v: 3 }, '3'],
314
+ ],
315
+ toKey,
316
+ fromKey,
317
+ ),
318
+ );
319
+ });
320
+ test('case 2', () => {
321
+ const s0 = IMapMapped.create(
322
+ [
323
+ [{ v: 1 }, '1'],
324
+ [{ v: 2 }, '2'],
325
+ [{ v: 3 }, '3'],
326
+ ],
327
+ toKey,
328
+ fromKey,
329
+ );
330
+
331
+ expect(s0.set({ v: 3 }, '3')).toStrictEqual(
332
+ IMapMapped.create(
333
+ [
334
+ [{ v: 1 }, '1'],
335
+ [{ v: 2 }, '2'],
336
+ [{ v: 3 }, '3'],
337
+ ],
338
+ toKey,
339
+ fromKey,
340
+ ),
341
+ );
342
+ expect(s0).toStrictEqual(
343
+ IMapMapped.create(
344
+ [
345
+ [{ v: 1 }, '1'],
346
+ [{ v: 2 }, '2'],
347
+ [{ v: 3 }, '3'],
348
+ ],
349
+ toKey,
350
+ fromKey,
351
+ ),
352
+ );
353
+ });
354
+ test('case 3', () => {
355
+ const s0 = IMapMapped.create([], toKey, fromKey);
356
+
357
+ expect(s0.set({ v: 1 }, '1')).toStrictEqual(
358
+ IMapMapped.create([[{ v: 1 }, '1']], toKey, fromKey),
359
+ );
360
+ expect(s0).toStrictEqual(IMapMapped.create([], toKey, fromKey));
361
+ });
362
+ });
363
+
364
+ describe('IMapMapped.update', () => {
365
+ test('should update existing key', () => {
366
+ const map = IMapMapped.create(
367
+ [[{ id: 1, type: 'user' }, 'Alice']],
368
+ testKeyToString,
369
+ stringToTestKey,
370
+ );
371
+ const updated = map.update({ id: 1, type: 'user' }, (name) =>
372
+ name.toUpperCase(),
373
+ );
374
+ expect(Optional.unwrap(updated.get({ id: 1, type: 'user' }))).toBe('ALICE');
375
+ });
376
+
377
+ test('should not update non-existent key', () => {
378
+ const map = IMapMapped.create(
379
+ [[{ id: 1, type: 'user' }, 'Alice']],
380
+ testKeyToString,
381
+ stringToTestKey,
382
+ );
383
+ const updated = map.update({ id: 2, type: 'user' }, (name) =>
384
+ name.toUpperCase(),
385
+ );
386
+ expect(updated).toBe(map);
387
+ expect(Optional.isNone(updated.get({ id: 2, type: 'user' }))).toBe(true);
388
+ });
389
+ });
390
+
391
+ describe('IMapMapped.delete', () => {
392
+ test('case 1', () => {
393
+ const s0 = IMapMapped.create(
394
+ [
395
+ [{ v: 1 }, '1'],
396
+ [{ v: 2 }, '2'],
397
+ [{ v: 3 }, '3'],
398
+ ],
399
+ toKey,
400
+ fromKey,
401
+ );
402
+
403
+ expect(s0.delete({ v: 10 })).toStrictEqual(
404
+ IMapMapped.create(
405
+ [
406
+ [{ v: 1 }, '1'],
407
+ [{ v: 2 }, '2'],
408
+ [{ v: 3 }, '3'],
409
+ ],
410
+ toKey,
411
+ fromKey,
412
+ ),
413
+ );
414
+ expect(s0).toStrictEqual(
415
+ IMapMapped.create(
416
+ [
417
+ [{ v: 1 }, '1'],
418
+ [{ v: 2 }, '2'],
419
+ [{ v: 3 }, '3'],
420
+ ],
421
+ toKey,
422
+ fromKey,
423
+ ),
424
+ );
425
+ });
426
+ test('case 2', () => {
427
+ const s0 = IMapMapped.create(
428
+ [
429
+ [{ v: 1 }, '1'],
430
+ [{ v: 2 }, '2'],
431
+ [{ v: 3 }, '3'],
432
+ ],
433
+ toKey,
434
+ fromKey,
435
+ );
436
+
437
+ expect(s0.delete({ v: 3 })).toStrictEqual(
438
+ IMapMapped.create(
439
+ [
440
+ [{ v: 1 }, '1'],
441
+ [{ v: 2 }, '2'],
442
+ ],
443
+ toKey,
444
+ fromKey,
445
+ ),
446
+ );
447
+ expect(s0).toStrictEqual(
448
+ IMapMapped.create(
449
+ [
450
+ [{ v: 1 }, '1'],
451
+ [{ v: 2 }, '2'],
452
+ [{ v: 3 }, '3'],
453
+ ],
454
+ toKey,
455
+ fromKey,
456
+ ),
457
+ );
458
+ });
459
+ test('case 3', () => {
460
+ const s0 = IMapMapped.create([], toKey, fromKey);
461
+
462
+ expect(s0.delete({ v: 1 })).toStrictEqual(
463
+ IMapMapped.create([], toKey, fromKey),
464
+ );
465
+ expect(s0).toStrictEqual(IMapMapped.create([], toKey, fromKey));
466
+ });
467
+
468
+ test('should delete entry if it exists', () => {
469
+ const map = IMapMapped.create(
470
+ [
471
+ [{ id: 1, type: 'user' }, 'Alice'],
472
+ [{ id: 2, type: 'admin' }, 'Bob'],
473
+ ],
474
+ testKeyToString,
475
+ stringToTestKey,
476
+ );
477
+ const updated = map.delete({ id: 2, type: 'admin' });
478
+
479
+ expect(updated).not.toBe(map); // Should return new instance
480
+ expect(updated.size).toBe(1);
481
+ expect(map.size).toBe(2); // Original unchanged
482
+ });
483
+ });
484
+
485
+ describe('IMapMapped.every', () => {
486
+ test('should return true when all values satisfy predicate', () => {
487
+ const map = IMapMapped.create(
488
+ [
489
+ [{ id: 1, type: 'user' }, 'Alice'],
490
+ [{ id: 2, type: 'admin' }, 'Bob'],
491
+ ],
492
+ testKeyToString,
493
+ stringToTestKey,
494
+ );
495
+ expect(map.every((value) => typeof value === 'string')).toBe(true);
496
+ });
497
+
498
+ test('should return false when some values do not satisfy predicate', () => {
499
+ const map = IMapMapped.create(
500
+ [
501
+ [{ id: 1, type: 'user' }, 'Alice'],
502
+ [{ id: 2, type: 'admin' }, 'Bob'],
503
+ ],
504
+ testKeyToString,
505
+ stringToTestKey,
506
+ );
507
+ expect(map.every((value) => value.length > 5)).toBe(false);
508
+ });
509
+
510
+ test('should return true for empty map', () => {
511
+ const map = IMapMapped.create<TestKey, string, string>(
512
+ [],
513
+ testKeyToString,
514
+ stringToTestKey,
515
+ );
516
+ expect(map.every((value) => value.length > 0)).toBe(true);
517
+ });
518
+ });
519
+
520
+ describe('IMapMapped.some', () => {
521
+ test('should return true when at least one value satisfies predicate', () => {
522
+ const map = IMapMapped.create(
523
+ [
524
+ [{ id: 1, type: 'user' }, 'Alice'],
525
+ [{ id: 2, type: 'admin' }, 'Bob'],
526
+ ],
527
+ testKeyToString,
528
+ stringToTestKey,
529
+ );
530
+ expect(map.some((value) => value === 'Alice')).toBe(true);
531
+ });
532
+
533
+ test('should return false when no values satisfy predicate', () => {
534
+ const map = IMapMapped.create(
535
+ [
536
+ [{ id: 1, type: 'user' }, 'Alice'],
537
+ [{ id: 2, type: 'admin' }, 'Bob'],
538
+ ],
539
+ testKeyToString,
540
+ stringToTestKey,
541
+ );
542
+ expect(map.some((value) => value === 'Charlie')).toBe(false);
543
+ });
544
+
545
+ test('should return false for empty map', () => {
546
+ const map = IMapMapped.create<TestKey, string, string>(
547
+ [],
548
+ testKeyToString,
549
+ stringToTestKey,
550
+ );
551
+ expect(map.some((value) => value.length > 0)).toBe(false);
552
+ });
553
+ });
554
+
555
+ describe('IMapMapped.withMutations', () => {
556
+ test('should apply multiple mutations', () => {
557
+ const map = IMapMapped.create<TestKey, string, string>(
558
+ [[{ id: 1, type: 'user' }, 'Alice']],
559
+ testKeyToString,
560
+ stringToTestKey,
561
+ );
562
+
563
+ const updated = map.withMutations([
564
+ { type: 'set', key: { id: 2, type: 'admin' }, value: 'Bob' },
565
+ {
566
+ type: 'update',
567
+ key: { id: 1, type: 'user' },
568
+ updater: (x) => x.toUpperCase(),
569
+ },
570
+ { type: 'delete', key: { id: 3, type: 'guest' } },
571
+ ]);
572
+
573
+ expect(updated.size).toBe(2);
574
+ expect(Optional.unwrap(updated.get({ id: 1, type: 'user' }))).toBe('ALICE');
575
+ expect(Optional.unwrap(updated.get({ id: 2, type: 'admin' }))).toBe('Bob');
576
+ });
577
+
578
+ test('should handle empty mutations array', () => {
579
+ const map = IMapMapped.create(
580
+ [[{ id: 1, type: 'user' }, 'Alice']],
581
+ testKeyToString,
582
+ stringToTestKey,
583
+ );
584
+ const updated = map.withMutations([]);
585
+ expect(updated.size).toBe(1);
586
+ expect(Optional.unwrap(updated.get({ id: 1, type: 'user' }))).toBe('Alice');
587
+ });
588
+ });
589
+
590
+ describe('IMapMapped.map', () => {
591
+ test('should transform values', () => {
592
+ const map = IMapMapped.create(
593
+ [
594
+ [{ id: 1, type: 'user' }, 'Alice'],
595
+ [{ id: 2, type: 'admin' }, 'Bob'],
596
+ ],
597
+ testKeyToString,
598
+ stringToTestKey,
599
+ );
600
+ const transformed = map.map((value) => value.toUpperCase());
601
+
602
+ expect(Optional.unwrap(transformed.get({ id: 1, type: 'user' }))).toBe(
603
+ 'ALICE',
604
+ );
605
+ expect(Optional.unwrap(transformed.get({ id: 2, type: 'admin' }))).toBe(
606
+ 'BOB',
607
+ );
608
+ });
609
+
610
+ test('should work with key parameter', () => {
611
+ const map = IMapMapped.create(
612
+ [
613
+ [{ id: 1, type: 'user' }, 'Alice'],
614
+ [{ id: 2, type: 'admin' }, 'Bob'],
615
+ ],
616
+ testKeyToString,
617
+ stringToTestKey,
618
+ );
619
+ const transformed = map.map((value, key) => `${key.type}:${value}`);
620
+
621
+ expect(Optional.unwrap(transformed.get({ id: 1, type: 'user' }))).toBe(
622
+ 'user:Alice',
623
+ );
624
+ expect(Optional.unwrap(transformed.get({ id: 2, type: 'admin' }))).toBe(
625
+ 'admin:Bob',
626
+ );
627
+ });
628
+ });
629
+
630
+ describe('IMapMapped.mapKeys', () => {
631
+ test('should transform keys', () => {
632
+ const map = IMapMapped.create(
633
+ [
634
+ [{ id: 1, type: 'user' }, 'Alice'],
635
+ [{ id: 2, type: 'admin' }, 'Bob'],
636
+ ],
637
+ testKeyToString,
638
+ stringToTestKey,
639
+ );
640
+ const transformed = map.mapKeys((key) => ({ ...key, id: key.id * 10 }));
641
+
642
+ expect(Optional.isNone(transformed.get({ id: 1, type: 'user' }))).toBe(
643
+ true,
644
+ );
645
+ expect(Optional.unwrap(transformed.get({ id: 10, type: 'user' }))).toBe(
646
+ 'Alice',
647
+ );
648
+ expect(Optional.unwrap(transformed.get({ id: 20, type: 'admin' }))).toBe(
649
+ 'Bob',
650
+ );
651
+ });
652
+ });
653
+
654
+ describe('IMapMapped.mapEntries', () => {
655
+ test('should transform both keys and values', () => {
656
+ const map = IMapMapped.create(
657
+ [
658
+ [{ id: 1, type: 'user' }, 'Alice'],
659
+ [{ id: 2, type: 'admin' }, 'Bob'],
660
+ ],
661
+ testKeyToString,
662
+ stringToTestKey,
663
+ );
664
+ const transformed = map.mapEntries(([key, value]) => [
665
+ { ...key, id: key.id * 10 },
666
+ value.toUpperCase(),
667
+ ]);
668
+
669
+ expect(Optional.unwrap(transformed.get({ id: 10, type: 'user' }))).toBe(
670
+ 'ALICE',
671
+ );
672
+ expect(Optional.unwrap(transformed.get({ id: 20, type: 'admin' }))).toBe(
673
+ 'BOB',
674
+ );
675
+ });
676
+ });
677
+
678
+ describe('IMapMapped.forEach', () => {
679
+ test('case 1', () => {
680
+ const s0 = IMapMapped.create(
681
+ [
682
+ [{ v: 1 }, '1'],
683
+ [{ v: 2 }, '2'],
684
+ [{ v: 3 }, '3'],
685
+ ],
686
+ toKey,
687
+ fromKey,
688
+ );
689
+ const keys = [{ v: 1 }, { v: 2 }, { v: 3 }];
690
+ const values = ['1', '2', '3'];
691
+
692
+ for (const [key, value] of s0.entries()) {
693
+ expect(keys).toContainEqual(key);
694
+ expect(values).toContainEqual(value);
695
+ }
696
+ });
697
+
698
+ test('should execute callback for each element', () => {
699
+ const map = IMapMapped.create(
700
+ [
701
+ [{ id: 1, type: 'user' }, 'Alice'],
702
+ [{ id: 2, type: 'admin' }, 'Bob'],
703
+ ],
704
+ testKeyToString,
705
+ stringToTestKey,
706
+ );
707
+ const collected: [TestKey, string][] = [];
708
+
709
+ map.forEach((value, key) => {
710
+ collected.push([key, value]);
711
+ });
712
+
713
+ expect(collected).toHaveLength(2);
714
+ expect(collected).toContainEqual([{ id: 1, type: 'user' }, 'Alice']);
715
+ expect(collected).toContainEqual([{ id: 2, type: 'admin' }, 'Bob']);
716
+ });
717
+ });
718
+
719
+ describe('IMapMapped.keys', () => {
720
+ test('case 1', () => {
721
+ const s0 = IMapMapped.create(
722
+ [
723
+ [{ v: 1 }, '1'],
724
+ [{ v: 2 }, '2'],
725
+ [{ v: 3 }, '3'],
726
+ ],
727
+ toKey,
728
+ fromKey,
729
+ );
730
+ const keys = [{ v: 1 }, { v: 2 }, { v: 3 }];
731
+
732
+ for (const k of s0.keys()) {
733
+ expect(keys).toContainEqual(k);
734
+ }
735
+ });
736
+ });
737
+
738
+ describe('IMapMapped.values', () => {
739
+ test('case 1', () => {
740
+ const s0 = IMapMapped.create(
741
+ [
742
+ [{ v: 1 }, '1'],
743
+ [{ v: 2 }, '2'],
744
+ [{ v: 3 }, '3'],
745
+ ],
746
+ toKey,
747
+ fromKey,
748
+ );
749
+ const values = ['1', '2', '3'];
750
+
751
+ for (const v of s0.values()) {
752
+ expect(values).toContainEqual(v);
753
+ }
754
+ });
755
+ });
756
+
757
+ describe('IMapMapped.entries', () => {
758
+ test('case 1', () => {
759
+ const s0 = IMapMapped.create(
760
+ [
761
+ [{ v: 1 }, '1'],
762
+ [{ v: 2 }, '2'],
763
+ [{ v: 3 }, '3'],
764
+ ],
765
+ toKey,
766
+ fromKey,
767
+ );
768
+ const keys = [{ v: 1 }, { v: 2 }, { v: 3 }];
769
+ const values = ['1', '2', '3'];
770
+
771
+ for (const [k, v] of s0.entries()) {
772
+ expect(keys).toContainEqual(k);
773
+ expect(values).toContainEqual(v);
774
+ }
775
+ });
776
+ });
777
+
778
+ describe('IMapMapped.toKeysArray', () => {
779
+ test('should return array of original keys', () => {
780
+ const map = IMapMapped.create(
781
+ [
782
+ [{ id: 1, type: 'user' }, 'Alice'],
783
+ [{ id: 2, type: 'admin' }, 'Bob'],
784
+ ],
785
+ testKeyToString,
786
+ stringToTestKey,
787
+ );
788
+ const keys = map.toKeysArray();
789
+ expect(keys).toHaveLength(2);
790
+ expect(keys).toContainEqual({ id: 1, type: 'user' });
791
+ expect(keys).toContainEqual({ id: 2, type: 'admin' });
792
+ });
793
+ });
794
+
795
+ describe('IMapMapped.toValuesArray', () => {
796
+ test('should return array of values', () => {
797
+ const map = IMapMapped.create(
798
+ [
799
+ [{ id: 1, type: 'user' }, 'Alice'],
800
+ [{ id: 2, type: 'admin' }, 'Bob'],
801
+ ],
802
+ testKeyToString,
803
+ stringToTestKey,
804
+ );
805
+ const values = map.toValuesArray();
806
+ expect(values).toHaveLength(2);
807
+ expect(values).toContain('Alice');
808
+ expect(values).toContain('Bob');
809
+ });
810
+ });
811
+
812
+ describe('IMapMapped.toEntriesArray', () => {
813
+ test('should return array of entries', () => {
814
+ const map = IMapMapped.create(
815
+ [
816
+ [{ id: 1, type: 'user' }, 'Alice'],
817
+ [{ id: 2, type: 'admin' }, 'Bob'],
818
+ ],
819
+ testKeyToString,
820
+ stringToTestKey,
821
+ );
822
+ const entries = map.toEntriesArray();
823
+ expect(entries).toHaveLength(2);
824
+ expect(entries).toContainEqual([{ id: 1, type: 'user' }, 'Alice']);
825
+ expect(entries).toContainEqual([{ id: 2, type: 'admin' }, 'Bob']);
826
+ });
827
+ });
828
+
829
+ describe('IMapMapped.toArray', () => {
830
+ test('should be alias for toEntriesArray', () => {
831
+ const map = IMapMapped.create(
832
+ [
833
+ [{ id: 1, type: 'user' }, 'Alice'],
834
+ [{ id: 2, type: 'admin' }, 'Bob'],
835
+ ],
836
+ testKeyToString,
837
+ stringToTestKey,
838
+ );
839
+ const entries = map.toArray();
840
+ const entriesArray = map.toEntriesArray();
841
+ expect(entries).toStrictEqual(entriesArray);
842
+ });
843
+ });
844
+
845
+ describe('IMapMapped.toRawMap', () => {
846
+ test('should return underlying map with transformed keys', () => {
847
+ const map = IMapMapped.create(
848
+ [
849
+ [{ id: 1, type: 'user' }, 'Alice'],
850
+ [{ id: 2, type: 'admin' }, 'Bob'],
851
+ ],
852
+ testKeyToString,
853
+ stringToTestKey,
854
+ );
855
+ const rawMap = map.toRawMap();
856
+ expect(rawMap.size).toBe(2);
857
+ expect(rawMap.get('user_1')).toBe('Alice');
858
+ expect(rawMap.get('admin_2')).toBe('Bob');
859
+ });
860
+ });