@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.
- package/CHANGELOG.md +8 -0
- package/package.json +91 -0
- package/rspack.config.mjs +65 -0
- package/src/assertions/index.ts +37 -0
- package/src/codegen/SlotPath.ts +20 -0
- package/src/codegen/blocks.ts +578 -0
- package/src/codegen/handlers/CloneHandlerBuilder.ts +13 -0
- package/src/codegen/handlers/CompareHandlerBuilder.ts +13 -0
- package/src/codegen/handlers/DeserializeHandlerBuilder.ts +20 -0
- package/src/codegen/handlers/EnableChangeTrackingHandlerBuilder.ts +13 -0
- package/src/codegen/handlers/EnrichmentHandlerBuilder.ts +28 -0
- package/src/codegen/handlers/FreezeHandlerBuilder.ts +13 -0
- package/src/codegen/handlers/HashHandlerBuilder.ts +25 -0
- package/src/codegen/handlers/HashTypeHandlerBuilder.ts +11 -0
- package/src/codegen/handlers/IdSelectorHandlerBuilder.ts +10 -0
- package/src/codegen/handlers/MergeHandlerBuilder.ts +19 -0
- package/src/codegen/handlers/PrepareHandlerBuilder.ts +23 -0
- package/src/codegen/handlers/SerializeHandlerBuilder.ts +15 -0
- package/src/codegen/handlers/StripHandlerBuilder.ts +18 -0
- package/src/codegen/handlers/clone/CloneObjectHandler.ts +29 -0
- package/src/codegen/handlers/clone/CloneValueHandler.ts +33 -0
- package/src/codegen/handlers/compare/CompareObjectHandler.ts +15 -0
- package/src/codegen/handlers/compare/CompareValueHandler.ts +27 -0
- package/src/codegen/handlers/deserialize/DeserializeComputedValueHandler.ts +16 -0
- package/src/codegen/handlers/deserialize/DeserializeDateHandler.ts +45 -0
- package/src/codegen/handlers/deserialize/DeserializeFunctionHandler.ts +16 -0
- package/src/codegen/handlers/deserialize/DeserializeObjectHandler.ts +29 -0
- package/src/codegen/handlers/deserialize/DeserializeValueHandler.ts +33 -0
- package/src/codegen/handlers/enableChangeTracking/EnableChangeTrackingObjectHandler.ts +20 -0
- package/src/codegen/handlers/enableChangeTracking/EnableChangeTrackingPrimitiveValueHandler.ts +15 -0
- package/src/codegen/handlers/enrichment/EnrichmentComputedValueHandler.ts +37 -0
- package/src/codegen/handlers/enrichment/EnrichmentDefaultFunctionHandler.ts +50 -0
- package/src/codegen/handlers/enrichment/EnrichmentDefaultValueHandler.ts +18 -0
- package/src/codegen/handlers/enrichment/EnrichmentFunctionHandler.ts +38 -0
- package/src/codegen/handlers/enrichment/EnrichmentNullableObjectHandler.ts +44 -0
- package/src/codegen/handlers/enrichment/EnrichmentObjectHandler.ts +29 -0
- package/src/codegen/handlers/enrichment/EnrichmentObjectIdentityHandler.ts +16 -0
- package/src/codegen/handlers/enrichment/EnrichmentPrimitiveHandler.ts +13 -0
- package/src/codegen/handlers/enrichment/EnrichmentPrimitiveIdentityHandler.ts +21 -0
- package/src/codegen/handlers/freeze/FreezeObjectHandler.ts +20 -0
- package/src/codegen/handlers/freeze/FreezePrimitiveValueHandler.ts +15 -0
- package/src/codegen/handlers/hash/HashComputedValueHandler.ts +15 -0
- package/src/codegen/handlers/hash/HashDateHandler.ts +26 -0
- package/src/codegen/handlers/hash/HashFunctionHandler.ts +16 -0
- package/src/codegen/handlers/hash/HashIdentityHandler.ts +15 -0
- package/src/codegen/handlers/hash/HashKeyHandler.ts +26 -0
- package/src/codegen/handlers/hash/HashValueHandler.ts +26 -0
- package/src/codegen/handlers/hashType/HashTypeValueHandler.ts +20 -0
- package/src/codegen/handlers/idSelector/IdSelectorValueHandler.ts +28 -0
- package/src/codegen/handlers/index.ts +13 -0
- package/src/codegen/handlers/merge/MergeComputedValueHandler.ts +37 -0
- package/src/codegen/handlers/merge/MergeDefaultFunctionHandler.ts +40 -0
- package/src/codegen/handlers/merge/MergeDefaultValueHandler.ts +32 -0
- package/src/codegen/handlers/merge/MergeFunctionHandler.ts +16 -0
- package/src/codegen/handlers/merge/MergePrimitiveHandler.ts +21 -0
- package/src/codegen/handlers/prepare/PrepareComputedValueHandler.ts +16 -0
- package/src/codegen/handlers/prepare/PrepareFunctionHandler.ts +16 -0
- package/src/codegen/handlers/prepare/PrepareIdentityHandler.ts +21 -0
- package/src/codegen/handlers/prepare/PrepareKeyHandler.ts +21 -0
- package/src/codegen/handlers/prepare/PrepareObjectHandler.ts +38 -0
- package/src/codegen/handlers/prepare/PrepareValueHandler.ts +36 -0
- package/src/codegen/handlers/serialize/SerializeDateHandler.ts +45 -0
- package/src/codegen/handlers/serialize/SerializeObjectHandler.ts +28 -0
- package/src/codegen/handlers/serialize/SerializeValueHandler.ts +33 -0
- package/src/codegen/handlers/strip/StripIdentityHandler.ts +15 -0
- package/src/codegen/handlers/strip/StripKeyHandler.ts +15 -0
- package/src/codegen/handlers/strip/StripObjectHandler.ts +35 -0
- package/src/codegen/handlers/strip/StripValueHandler.ts +36 -0
- package/src/codegen/handlers/types.ts +119 -0
- package/src/codegen/index.ts +2 -0
- package/src/codegen/types.ts +2 -0
- package/src/codegen/utils.ts +74 -0
- package/src/collections/Changes.test.ts +337 -0
- package/src/collections/Changes.ts +177 -0
- package/src/collections/IdSet.ts +28 -0
- package/src/collections/MemoryDataCollection.test.ts +424 -0
- package/src/collections/MemoryDataCollection.ts +134 -0
- package/src/collections/SchemaCollection.ts +22 -0
- package/src/collections/TagCollection.test.ts +443 -0
- package/src/collections/TagCollection.ts +62 -0
- package/src/collections/index.ts +5 -0
- package/src/errors/SchemaError.ts +6 -0
- package/src/errors/index.ts +1 -0
- package/src/expressions/index.ts +3 -0
- package/src/expressions/parser.test.ts +913 -0
- package/src/expressions/parser.ts +661 -0
- package/src/expressions/types.ts +184 -0
- package/src/expressions/utils.test.ts +346 -0
- package/src/expressions/utils.ts +59 -0
- package/src/index.ts +12 -0
- package/src/performance/index.ts +26 -0
- package/src/pipeline/SyncronousQueue.test.ts +269 -0
- package/src/pipeline/SyncronousQueue.ts +30 -0
- package/src/pipeline/TrampolinePipeline.test.ts +374 -0
- package/src/pipeline/TrampolinePipeline.ts +437 -0
- package/src/pipeline/index.ts +2 -0
- package/src/plugins/EphemeralDataPlugin.ts +132 -0
- package/src/plugins/capabilities/DbPluginCapability.ts +111 -0
- package/src/plugins/capabilities/index.ts +2 -0
- package/src/plugins/capabilities/logging/DbPluginLoggingCapability.ts +261 -0
- package/src/plugins/capabilities/logging/index.ts +1 -0
- package/src/plugins/index.ts +6 -0
- package/src/plugins/query/Query.ts +67 -0
- package/src/plugins/query/QueryOptionsCollection.test.ts +86 -0
- package/src/plugins/query/QueryOptionsCollection.ts +154 -0
- package/src/plugins/query/index.ts +3 -0
- package/src/plugins/query/types.ts +46 -0
- package/src/plugins/replication/OptimisticReplicationDbPlugin.ts +202 -0
- package/src/plugins/replication/ReplicationDbPlugin.ts +137 -0
- package/src/plugins/replication/index.ts +3 -0
- package/src/plugins/replication/types.ts +5 -0
- package/src/plugins/translators/DataTranslator.ts +46 -0
- package/src/plugins/translators/JsonTranslator.test.ts +618 -0
- package/src/plugins/translators/JsonTranslator.ts +211 -0
- package/src/plugins/translators/SqlTranslator.ts +45 -0
- package/src/plugins/translators/index.ts +3 -0
- package/src/plugins/types.ts +114 -0
- package/src/results/Result.ts +91 -0
- package/src/results/index.ts +3 -0
- package/src/results/types.ts +17 -0
- package/src/results/utils.ts +15 -0
- package/src/schema/PropertyInfo.test.ts +479 -0
- package/src/schema/PropertyInfo.ts +374 -0
- package/src/schema/SchemaDefinition.ts +626 -0
- package/src/schema/builder.ts +18 -0
- package/src/schema/index.ts +7 -0
- package/src/schema/property/base/SchemaBase.ts +49 -0
- package/src/schema/property/base/index.ts +1 -0
- package/src/schema/property/modifiers/SchemaDefault.ts +29 -0
- package/src/schema/property/modifiers/SchemaDeserialize.ts +23 -0
- package/src/schema/property/modifiers/SchemaDistinct.ts +14 -0
- package/src/schema/property/modifiers/SchemaFrom.ts +54 -0
- package/src/schema/property/modifiers/SchemaIdentity.ts +13 -0
- package/src/schema/property/modifiers/SchemaIndex.ts +60 -0
- package/src/schema/property/modifiers/SchemaKey.ts +28 -0
- package/src/schema/property/modifiers/SchemaNullable.ts +18 -0
- package/src/schema/property/modifiers/SchemaOptional.ts +19 -0
- package/src/schema/property/modifiers/SchemaReadonly.ts +34 -0
- package/src/schema/property/modifiers/SchemaSerialize.ts +18 -0
- package/src/schema/property/modifiers/SchemaTracked.ts +14 -0
- package/src/schema/property/modifiers/index.ts +12 -0
- package/src/schema/property/types/SchemaArray.ts +50 -0
- package/src/schema/property/types/SchemaBoolean.ts +59 -0
- package/src/schema/property/types/SchemaDate.ts +58 -0
- package/src/schema/property/types/SchemaNumber.ts +69 -0
- package/src/schema/property/types/SchemaObject.ts +44 -0
- package/src/schema/property/types/SchemaString.ts +69 -0
- package/src/schema/property/types/index.ts +6 -0
- package/src/schema/table/SchemaComputed.ts +20 -0
- package/src/schema/table/SchemaFunction.ts +15 -0
- package/src/schema/table/index.ts +2 -0
- package/src/schema/types.ts +238 -0
- package/src/types/index.ts +5 -0
- package/src/utilities/arrays.test.ts +312 -0
- package/src/utilities/arrays.ts +12 -0
- package/src/utilities/dates.test.ts +388 -0
- package/src/utilities/dates.ts +11 -0
- package/src/utilities/dbPluginEventUtils.ts +44 -0
- package/src/utilities/index.ts +10 -0
- package/src/utilities/objects.ts +7 -0
- package/src/utilities/queryOptionsCollection.ts +16 -0
- package/src/utilities/replication.ts +23 -0
- package/src/utilities/runtime.ts +3 -0
- package/src/utilities/strings.ts +18 -0
- package/src/utilities/types.ts +1 -0
- package/src/utilities/uuid.ts +56 -0
- package/tsconfig.json +28 -0
- 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
|
+
}
|