@routier/core 0.0.1-alpha.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.
Files changed (168) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/package.json +91 -0
  3. package/rspack.config.mjs +65 -0
  4. package/src/assertions/index.ts +37 -0
  5. package/src/codegen/SlotPath.ts +20 -0
  6. package/src/codegen/blocks.ts +578 -0
  7. package/src/codegen/handlers/CloneHandlerBuilder.ts +13 -0
  8. package/src/codegen/handlers/CompareHandlerBuilder.ts +13 -0
  9. package/src/codegen/handlers/DeserializeHandlerBuilder.ts +20 -0
  10. package/src/codegen/handlers/EnableChangeTrackingHandlerBuilder.ts +13 -0
  11. package/src/codegen/handlers/EnrichmentHandlerBuilder.ts +28 -0
  12. package/src/codegen/handlers/FreezeHandlerBuilder.ts +13 -0
  13. package/src/codegen/handlers/HashHandlerBuilder.ts +25 -0
  14. package/src/codegen/handlers/HashTypeHandlerBuilder.ts +11 -0
  15. package/src/codegen/handlers/IdSelectorHandlerBuilder.ts +10 -0
  16. package/src/codegen/handlers/MergeHandlerBuilder.ts +19 -0
  17. package/src/codegen/handlers/PrepareHandlerBuilder.ts +23 -0
  18. package/src/codegen/handlers/SerializeHandlerBuilder.ts +15 -0
  19. package/src/codegen/handlers/StripHandlerBuilder.ts +18 -0
  20. package/src/codegen/handlers/clone/CloneObjectHandler.ts +29 -0
  21. package/src/codegen/handlers/clone/CloneValueHandler.ts +33 -0
  22. package/src/codegen/handlers/compare/CompareObjectHandler.ts +15 -0
  23. package/src/codegen/handlers/compare/CompareValueHandler.ts +27 -0
  24. package/src/codegen/handlers/deserialize/DeserializeComputedValueHandler.ts +16 -0
  25. package/src/codegen/handlers/deserialize/DeserializeDateHandler.ts +45 -0
  26. package/src/codegen/handlers/deserialize/DeserializeFunctionHandler.ts +16 -0
  27. package/src/codegen/handlers/deserialize/DeserializeObjectHandler.ts +29 -0
  28. package/src/codegen/handlers/deserialize/DeserializeValueHandler.ts +33 -0
  29. package/src/codegen/handlers/enableChangeTracking/EnableChangeTrackingObjectHandler.ts +20 -0
  30. package/src/codegen/handlers/enableChangeTracking/EnableChangeTrackingPrimitiveValueHandler.ts +15 -0
  31. package/src/codegen/handlers/enrichment/EnrichmentComputedValueHandler.ts +37 -0
  32. package/src/codegen/handlers/enrichment/EnrichmentDefaultFunctionHandler.ts +50 -0
  33. package/src/codegen/handlers/enrichment/EnrichmentDefaultValueHandler.ts +18 -0
  34. package/src/codegen/handlers/enrichment/EnrichmentFunctionHandler.ts +38 -0
  35. package/src/codegen/handlers/enrichment/EnrichmentNullableObjectHandler.ts +44 -0
  36. package/src/codegen/handlers/enrichment/EnrichmentObjectHandler.ts +29 -0
  37. package/src/codegen/handlers/enrichment/EnrichmentObjectIdentityHandler.ts +16 -0
  38. package/src/codegen/handlers/enrichment/EnrichmentPrimitiveHandler.ts +13 -0
  39. package/src/codegen/handlers/enrichment/EnrichmentPrimitiveIdentityHandler.ts +21 -0
  40. package/src/codegen/handlers/freeze/FreezeObjectHandler.ts +20 -0
  41. package/src/codegen/handlers/freeze/FreezePrimitiveValueHandler.ts +15 -0
  42. package/src/codegen/handlers/hash/HashComputedValueHandler.ts +15 -0
  43. package/src/codegen/handlers/hash/HashDateHandler.ts +26 -0
  44. package/src/codegen/handlers/hash/HashFunctionHandler.ts +16 -0
  45. package/src/codegen/handlers/hash/HashIdentityHandler.ts +15 -0
  46. package/src/codegen/handlers/hash/HashKeyHandler.ts +26 -0
  47. package/src/codegen/handlers/hash/HashValueHandler.ts +26 -0
  48. package/src/codegen/handlers/hashType/HashTypeValueHandler.ts +20 -0
  49. package/src/codegen/handlers/idSelector/IdSelectorValueHandler.ts +28 -0
  50. package/src/codegen/handlers/index.ts +13 -0
  51. package/src/codegen/handlers/merge/MergeComputedValueHandler.ts +37 -0
  52. package/src/codegen/handlers/merge/MergeDefaultFunctionHandler.ts +40 -0
  53. package/src/codegen/handlers/merge/MergeDefaultValueHandler.ts +32 -0
  54. package/src/codegen/handlers/merge/MergeFunctionHandler.ts +16 -0
  55. package/src/codegen/handlers/merge/MergePrimitiveHandler.ts +21 -0
  56. package/src/codegen/handlers/prepare/PrepareComputedValueHandler.ts +16 -0
  57. package/src/codegen/handlers/prepare/PrepareFunctionHandler.ts +16 -0
  58. package/src/codegen/handlers/prepare/PrepareIdentityHandler.ts +21 -0
  59. package/src/codegen/handlers/prepare/PrepareKeyHandler.ts +21 -0
  60. package/src/codegen/handlers/prepare/PrepareObjectHandler.ts +38 -0
  61. package/src/codegen/handlers/prepare/PrepareValueHandler.ts +36 -0
  62. package/src/codegen/handlers/serialize/SerializeDateHandler.ts +45 -0
  63. package/src/codegen/handlers/serialize/SerializeObjectHandler.ts +28 -0
  64. package/src/codegen/handlers/serialize/SerializeValueHandler.ts +33 -0
  65. package/src/codegen/handlers/strip/StripIdentityHandler.ts +15 -0
  66. package/src/codegen/handlers/strip/StripKeyHandler.ts +15 -0
  67. package/src/codegen/handlers/strip/StripObjectHandler.ts +35 -0
  68. package/src/codegen/handlers/strip/StripValueHandler.ts +36 -0
  69. package/src/codegen/handlers/types.ts +119 -0
  70. package/src/codegen/index.ts +2 -0
  71. package/src/codegen/types.ts +2 -0
  72. package/src/codegen/utils.ts +74 -0
  73. package/src/collections/Changes.test.ts +337 -0
  74. package/src/collections/Changes.ts +177 -0
  75. package/src/collections/IdSet.ts +28 -0
  76. package/src/collections/MemoryDataCollection.test.ts +424 -0
  77. package/src/collections/MemoryDataCollection.ts +134 -0
  78. package/src/collections/SchemaCollection.ts +22 -0
  79. package/src/collections/TagCollection.test.ts +443 -0
  80. package/src/collections/TagCollection.ts +62 -0
  81. package/src/collections/index.ts +5 -0
  82. package/src/errors/SchemaError.ts +6 -0
  83. package/src/errors/index.ts +1 -0
  84. package/src/expressions/index.ts +3 -0
  85. package/src/expressions/parser.test.ts +913 -0
  86. package/src/expressions/parser.ts +661 -0
  87. package/src/expressions/types.ts +184 -0
  88. package/src/expressions/utils.test.ts +346 -0
  89. package/src/expressions/utils.ts +59 -0
  90. package/src/index.ts +12 -0
  91. package/src/performance/index.ts +26 -0
  92. package/src/pipeline/SyncronousQueue.test.ts +269 -0
  93. package/src/pipeline/SyncronousQueue.ts +30 -0
  94. package/src/pipeline/TrampolinePipeline.test.ts +374 -0
  95. package/src/pipeline/TrampolinePipeline.ts +437 -0
  96. package/src/pipeline/index.ts +2 -0
  97. package/src/plugins/EphemeralDataPlugin.ts +132 -0
  98. package/src/plugins/capabilities/DbPluginCapability.ts +111 -0
  99. package/src/plugins/capabilities/index.ts +2 -0
  100. package/src/plugins/capabilities/logging/DbPluginLoggingCapability.ts +261 -0
  101. package/src/plugins/capabilities/logging/index.ts +1 -0
  102. package/src/plugins/index.ts +6 -0
  103. package/src/plugins/query/Query.ts +67 -0
  104. package/src/plugins/query/QueryOptionsCollection.test.ts +86 -0
  105. package/src/plugins/query/QueryOptionsCollection.ts +154 -0
  106. package/src/plugins/query/index.ts +3 -0
  107. package/src/plugins/query/types.ts +46 -0
  108. package/src/plugins/replication/OptimisticReplicationDbPlugin.ts +202 -0
  109. package/src/plugins/replication/ReplicationDbPlugin.ts +137 -0
  110. package/src/plugins/replication/index.ts +3 -0
  111. package/src/plugins/replication/types.ts +5 -0
  112. package/src/plugins/translators/DataTranslator.ts +46 -0
  113. package/src/plugins/translators/JsonTranslator.test.ts +618 -0
  114. package/src/plugins/translators/JsonTranslator.ts +211 -0
  115. package/src/plugins/translators/SqlTranslator.ts +45 -0
  116. package/src/plugins/translators/index.ts +3 -0
  117. package/src/plugins/types.ts +114 -0
  118. package/src/results/Result.ts +91 -0
  119. package/src/results/index.ts +3 -0
  120. package/src/results/types.ts +17 -0
  121. package/src/results/utils.ts +15 -0
  122. package/src/schema/PropertyInfo.test.ts +479 -0
  123. package/src/schema/PropertyInfo.ts +374 -0
  124. package/src/schema/SchemaDefinition.ts +626 -0
  125. package/src/schema/builder.ts +18 -0
  126. package/src/schema/index.ts +7 -0
  127. package/src/schema/property/base/SchemaBase.ts +49 -0
  128. package/src/schema/property/base/index.ts +1 -0
  129. package/src/schema/property/modifiers/SchemaDefault.ts +29 -0
  130. package/src/schema/property/modifiers/SchemaDeserialize.ts +23 -0
  131. package/src/schema/property/modifiers/SchemaDistinct.ts +14 -0
  132. package/src/schema/property/modifiers/SchemaFrom.ts +54 -0
  133. package/src/schema/property/modifiers/SchemaIdentity.ts +13 -0
  134. package/src/schema/property/modifiers/SchemaIndex.ts +60 -0
  135. package/src/schema/property/modifiers/SchemaKey.ts +28 -0
  136. package/src/schema/property/modifiers/SchemaNullable.ts +18 -0
  137. package/src/schema/property/modifiers/SchemaOptional.ts +19 -0
  138. package/src/schema/property/modifiers/SchemaReadonly.ts +34 -0
  139. package/src/schema/property/modifiers/SchemaSerialize.ts +18 -0
  140. package/src/schema/property/modifiers/SchemaTracked.ts +14 -0
  141. package/src/schema/property/modifiers/index.ts +12 -0
  142. package/src/schema/property/types/SchemaArray.ts +50 -0
  143. package/src/schema/property/types/SchemaBoolean.ts +59 -0
  144. package/src/schema/property/types/SchemaDate.ts +58 -0
  145. package/src/schema/property/types/SchemaNumber.ts +69 -0
  146. package/src/schema/property/types/SchemaObject.ts +44 -0
  147. package/src/schema/property/types/SchemaString.ts +69 -0
  148. package/src/schema/property/types/index.ts +6 -0
  149. package/src/schema/table/SchemaComputed.ts +20 -0
  150. package/src/schema/table/SchemaFunction.ts +15 -0
  151. package/src/schema/table/index.ts +2 -0
  152. package/src/schema/types.ts +238 -0
  153. package/src/types/index.ts +5 -0
  154. package/src/utilities/arrays.test.ts +312 -0
  155. package/src/utilities/arrays.ts +12 -0
  156. package/src/utilities/dates.test.ts +388 -0
  157. package/src/utilities/dates.ts +11 -0
  158. package/src/utilities/dbPluginEventUtils.ts +44 -0
  159. package/src/utilities/index.ts +10 -0
  160. package/src/utilities/objects.ts +7 -0
  161. package/src/utilities/queryOptionsCollection.ts +16 -0
  162. package/src/utilities/replication.ts +23 -0
  163. package/src/utilities/runtime.ts +3 -0
  164. package/src/utilities/strings.ts +18 -0
  165. package/src/utilities/types.ts +1 -0
  166. package/src/utilities/uuid.ts +56 -0
  167. package/tsconfig.json +28 -0
  168. package/vitest.config.ts +11 -0
@@ -0,0 +1,312 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { toMap } from './arrays';
3
+
4
+ interface TestUser {
5
+ id: number;
6
+ name: string;
7
+ email: string;
8
+ age: number;
9
+ isActive: boolean;
10
+ }
11
+
12
+ interface TestProduct {
13
+ sku: string;
14
+ name: string;
15
+ price: number;
16
+ category: string;
17
+ }
18
+
19
+ describe('toMap', () => {
20
+ describe('basic functionality', () => {
21
+ it('should convert array to map using string key', () => {
22
+ const users: TestUser[] = [
23
+ { id: 1, name: 'Alice', email: 'alice@example.com', age: 25, isActive: true },
24
+ { id: 2, name: 'Bob', email: 'bob@example.com', age: 30, isActive: false },
25
+ { id: 3, name: 'Charlie', email: 'charlie@example.com', age: 35, isActive: true }
26
+ ];
27
+
28
+ const result = toMap(users, (user) => user.name);
29
+
30
+ expect(result).toBeInstanceOf(Map);
31
+ expect(result.size).toBe(3);
32
+ expect(result.get('Alice')).toEqual(users[0]);
33
+ expect(result.get('Bob')).toEqual(users[1]);
34
+ expect(result.get('Charlie')).toEqual(users[2]);
35
+ });
36
+
37
+ it('should convert array to map using number key', () => {
38
+ const users: TestUser[] = [
39
+ { id: 1, name: 'Alice', email: 'alice@example.com', age: 25, isActive: true },
40
+ { id: 2, name: 'Bob', email: 'bob@example.com', age: 30, isActive: false },
41
+ { id: 3, name: 'Charlie', email: 'charlie@example.com', age: 35, isActive: true }
42
+ ];
43
+
44
+ const result = toMap(users, (user) => user.id);
45
+
46
+ expect(result).toBeInstanceOf(Map);
47
+ expect(result.size).toBe(3);
48
+ expect(result.get(1)).toEqual(users[0]);
49
+ expect(result.get(2)).toEqual(users[1]);
50
+ expect(result.get(3)).toEqual(users[2]);
51
+ });
52
+
53
+ it('should convert array to map using boolean key', () => {
54
+ const users: TestUser[] = [
55
+ { id: 1, name: 'Alice', email: 'alice@example.com', age: 25, isActive: true },
56
+ { id: 2, name: 'Bob', email: 'bob@example.com', age: 30, isActive: false },
57
+ { id: 3, name: 'Charlie', email: 'charlie@example.com', age: 35, isActive: true }
58
+ ];
59
+
60
+ const result = toMap(users, (user) => user.isActive);
61
+
62
+ expect(result).toBeInstanceOf(Map);
63
+ expect(result.size).toBe(2); // Only 2 unique boolean values
64
+ expect(result.get(true)).toEqual(users[2]); // Last one wins for duplicate keys
65
+ expect(result.get(false)).toEqual(users[1]);
66
+ });
67
+ });
68
+
69
+ describe('edge cases', () => {
70
+ it('should handle empty array', () => {
71
+ const result = toMap([], (item: any) => item.id);
72
+
73
+ expect(result).toBeInstanceOf(Map);
74
+ expect(result.size).toBe(0);
75
+ });
76
+
77
+ it('should handle single item array', () => {
78
+ const users: TestUser[] = [
79
+ { id: 1, name: 'Alice', email: 'alice@example.com', age: 25, isActive: true }
80
+ ];
81
+
82
+ const result = toMap(users, (user) => user.name);
83
+
84
+ expect(result).toBeInstanceOf(Map);
85
+ expect(result.size).toBe(1);
86
+ expect(result.get('Alice')).toEqual(users[0]);
87
+ });
88
+
89
+ it('should handle duplicate keys (last item wins)', () => {
90
+ const users: TestUser[] = [
91
+ { id: 1, name: 'Alice', email: 'alice@example.com', age: 25, isActive: true },
92
+ { id: 2, name: 'Alice', email: 'alice2@example.com', age: 30, isActive: false },
93
+ { id: 3, name: 'Bob', email: 'bob@example.com', age: 35, isActive: true }
94
+ ];
95
+
96
+ const result = toMap(users, (user) => user.name);
97
+
98
+ expect(result).toBeInstanceOf(Map);
99
+ expect(result.size).toBe(2); // Only 2 unique names
100
+ expect(result.get('Alice')).toEqual(users[1]); // Last Alice wins
101
+ expect(result.get('Bob')).toEqual(users[2]);
102
+ });
103
+
104
+ it('should handle null and undefined values in key selector', () => {
105
+ const users: TestUser[] = [
106
+ { id: 1, name: 'Alice', email: 'alice@example.com', age: 25, isActive: true },
107
+ { id: 2, name: 'Bob', email: 'bob@example.com', age: 30, isActive: false }
108
+ ];
109
+
110
+ // Test with key selector that could return null/undefined
111
+ const result = toMap(users, (user) => user.age > 30 ? 'old' : 'young');
112
+
113
+ expect(result).toBeInstanceOf(Map);
114
+ expect(result.size).toBe(1); // Both users are 'young' (age <= 30)
115
+ expect(result.get('young')).toEqual(users[1]); // Last 'young' user wins
116
+ });
117
+ });
118
+
119
+ describe('complex key selectors', () => {
120
+ it('should handle computed keys', () => {
121
+ const products: TestProduct[] = [
122
+ { sku: 'ABC123', name: 'Laptop', price: 999, category: 'Electronics' },
123
+ { sku: 'DEF456', name: 'Mouse', price: 25, category: 'Electronics' },
124
+ { sku: 'GHI789', name: 'Book', price: 15, category: 'Books' }
125
+ ];
126
+
127
+ const result = toMap(products, (product) => `${product.category}-${product.sku}`);
128
+
129
+ expect(result).toBeInstanceOf(Map);
130
+ expect(result.size).toBe(3);
131
+ expect(result.get('Electronics-ABC123')).toEqual(products[0]);
132
+ expect(result.get('Electronics-DEF456')).toEqual(products[1]);
133
+ expect(result.get('Books-GHI789')).toEqual(products[2]);
134
+ });
135
+
136
+ it('should handle conditional key selection', () => {
137
+ const users: TestUser[] = [
138
+ { id: 1, name: 'Alice', email: 'alice@example.com', age: 25, isActive: true },
139
+ { id: 2, name: 'Bob', email: 'bob@example.com', age: 30, isActive: false },
140
+ { id: 3, name: 'Charlie', email: 'charlie@example.com', age: 35, isActive: true }
141
+ ];
142
+
143
+ const result = toMap(users, (user) => user.isActive ? `active-${user.id}` : `inactive-${user.id}`);
144
+
145
+ expect(result).toBeInstanceOf(Map);
146
+ expect(result.size).toBe(3);
147
+ expect(result.get('active-1')).toEqual(users[0]);
148
+ expect(result.get('inactive-2')).toEqual(users[1]);
149
+ expect(result.get('active-3')).toEqual(users[2]);
150
+ });
151
+
152
+ it('should handle nested property access', () => {
153
+ const users: TestUser[] = [
154
+ { id: 1, name: 'Alice', email: 'alice@example.com', age: 25, isActive: true },
155
+ { id: 2, name: 'Bob', email: 'bob@example.com', age: 30, isActive: false }
156
+ ];
157
+
158
+ // Simulate nested object access
159
+ const result = toMap(users, (user) => user.email.split('@')[0]);
160
+
161
+ expect(result).toBeInstanceOf(Map);
162
+ expect(result.size).toBe(2);
163
+ expect(result.get('alice')).toEqual(users[0]);
164
+ expect(result.get('bob')).toEqual(users[1]);
165
+ });
166
+ });
167
+
168
+ describe('type safety', () => {
169
+ it('should maintain type safety with different key types', () => {
170
+ const users: TestUser[] = [
171
+ { id: 1, name: 'Alice', email: 'alice@example.com', age: 25, isActive: true }
172
+ ];
173
+
174
+ // String key
175
+ const stringMap = toMap(users, (user) => user.name);
176
+ expect(stringMap.get('Alice')).toBeDefined();
177
+
178
+ // Number key
179
+ const numberMap = toMap(users, (user) => user.id);
180
+ expect(numberMap.get(1)).toBeDefined();
181
+
182
+ // Boolean key
183
+ const booleanMap = toMap(users, (user) => user.isActive);
184
+ expect(booleanMap.get(true)).toBeDefined();
185
+ });
186
+
187
+ it('should handle mixed key types in same function', () => {
188
+ const users: TestUser[] = [
189
+ { id: 1, name: 'Alice', email: 'alice@example.com', age: 25, isActive: true },
190
+ { id: 2, name: 'Bob', email: 'bob@example.com', age: 30, isActive: false }
191
+ ];
192
+
193
+ const result = toMap(users, (user) => user.isActive ? user.id : user.name);
194
+
195
+ expect(result).toBeInstanceOf(Map);
196
+ expect(result.size).toBe(2);
197
+ expect(result.get(1)).toEqual(users[0]);
198
+ expect(result.get('Bob')).toEqual(users[1]);
199
+ });
200
+ });
201
+
202
+ describe('performance characteristics', () => {
203
+ it('should handle large arrays efficiently', () => {
204
+ const largeArray = Array.from({ length: 10000 }, (_, i) => ({
205
+ id: i,
206
+ name: `User${i}`,
207
+ email: `user${i}@example.com`,
208
+ age: 20 + (i % 50),
209
+ isActive: i % 2 === 0
210
+ }));
211
+
212
+ const start = performance.now();
213
+ const result = toMap(largeArray, (user) => user.id);
214
+ const end = performance.now();
215
+
216
+ expect(result).toBeInstanceOf(Map);
217
+ expect(result.size).toBe(10000);
218
+ expect(result.get(0)).toEqual(largeArray[0]);
219
+ expect(result.get(9999)).toEqual(largeArray[9999]);
220
+ expect(end - start).toBeLessThan(100); // Should complete in under 100ms
221
+ });
222
+
223
+ it('should handle many duplicate keys efficiently', () => {
224
+ const arrayWithDuplicates = Array.from({ length: 1000 }, (_, i) => ({
225
+ id: i,
226
+ name: i % 10 === 0 ? 'Alice' : `User${i}`,
227
+ email: `user${i}@example.com`,
228
+ age: 20 + (i % 50),
229
+ isActive: i % 2 === 0
230
+ }));
231
+
232
+ const start = performance.now();
233
+ const result = toMap(arrayWithDuplicates, (user) => user.name);
234
+ const end = performance.now();
235
+
236
+ expect(result).toBeInstanceOf(Map);
237
+ expect(result.size).toBeLessThan(1000); // Fewer unique keys due to duplicates
238
+ expect(result.get('Alice')).toBeDefined(); // Last Alice should be in map
239
+ expect(end - start).toBeLessThan(50); // Should complete in under 50ms
240
+ });
241
+ });
242
+
243
+ describe('error handling', () => {
244
+ it('should handle key selector that throws error', () => {
245
+ const users: TestUser[] = [
246
+ { id: 1, name: 'Alice', email: 'alice@example.com', age: 25, isActive: true },
247
+ { id: 2, name: 'Bob', email: 'bob@example.com', age: 30, isActive: false }
248
+ ];
249
+
250
+ expect(() => {
251
+ toMap(users, (user) => {
252
+ if (user.id === 1) {
253
+ throw new Error('Test error');
254
+ }
255
+ return user.name;
256
+ });
257
+ }).toThrow('Test error');
258
+ });
259
+
260
+ it('should handle null/undefined in input array', () => {
261
+ const users: TestUser[] = [
262
+ { id: 1, name: 'Alice', email: 'alice@example.com', age: 25, isActive: true },
263
+ null as any,
264
+ { id: 2, name: 'Bob', email: 'bob@example.com', age: 30, isActive: false }
265
+ ];
266
+
267
+ expect(() => {
268
+ toMap(users, (user) => user.name);
269
+ }).toThrow(); // Should throw when trying to access .name on null
270
+ });
271
+ });
272
+
273
+ describe('real-world scenarios', () => {
274
+ it('should work with user management system', () => {
275
+ const users: TestUser[] = [
276
+ { id: 1, name: 'Alice', email: 'alice@example.com', age: 25, isActive: true },
277
+ { id: 2, name: 'Bob', email: 'bob@example.com', age: 30, isActive: false },
278
+ { id: 3, name: 'Charlie', email: 'charlie@example.com', age: 35, isActive: true }
279
+ ];
280
+
281
+ // Create maps for different lookup scenarios
282
+ const idMap = toMap(users, (user) => user.id);
283
+ const emailMap = toMap(users, (user) => user.email);
284
+ const activeStatusMap = toMap(users, (user) => user.isActive);
285
+
286
+ expect(idMap.get(1)?.name).toBe('Alice');
287
+ expect(emailMap.get('bob@example.com')?.id).toBe(2);
288
+ expect(activeStatusMap.get(true)?.name).toBe('Charlie'); // Last active user
289
+ });
290
+
291
+ it('should work with e-commerce product catalog', () => {
292
+ const products: TestProduct[] = [
293
+ { sku: 'LAPTOP001', name: 'Gaming Laptop', price: 1299, category: 'Electronics' },
294
+ { sku: 'MOUSE001', name: 'Wireless Mouse', price: 29, category: 'Electronics' },
295
+ { sku: 'BOOK001', name: 'Programming Guide', price: 45, category: 'Books' },
296
+ { sku: 'LAPTOP002', name: 'Business Laptop', price: 899, category: 'Electronics' }
297
+ ];
298
+
299
+ // Create maps for different lookup scenarios
300
+ const skuMap = toMap(products, (product) => product.sku);
301
+ const categoryMap = toMap(products, (product) => product.category);
302
+ const priceRangeMap = toMap(products, (product) =>
303
+ product.price > 1000 ? 'premium' : product.price > 100 ? 'mid-range' : 'budget'
304
+ );
305
+
306
+ expect(skuMap.get('LAPTOP001')?.name).toBe('Gaming Laptop');
307
+ expect(categoryMap.get('Electronics')?.name).toBe('Business Laptop'); // Last electronics product
308
+ expect(priceRangeMap.get('premium')?.sku).toBe('LAPTOP001');
309
+ expect(priceRangeMap.get('budget')?.sku).toBe('BOOK001'); // Last budget item wins
310
+ });
311
+ });
312
+ });
@@ -0,0 +1,12 @@
1
+ export const toMap = <T extends {}>(data: T[], keySelector: (item: T) => T[keyof T] | string) => {
2
+
3
+ const result = new Map<T[keyof T] | string, T>();
4
+
5
+ for (let i = 0; i < data.length; i++) {
6
+ const item = data[i];
7
+ const key = keySelector(item);
8
+ result.set(key, item);
9
+ }
10
+
11
+ return result;
12
+ }