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.
- package/LICENSE +201 -0
- package/README.md +534 -0
- package/package.json +101 -0
- package/src/array/array-utils-creation.test.mts +443 -0
- package/src/array/array-utils-modification.test.mts +197 -0
- package/src/array/array-utils-overload-type-error.test.mts +149 -0
- package/src/array/array-utils-reducing-value.test.mts +425 -0
- package/src/array/array-utils-search.test.mts +169 -0
- package/src/array/array-utils-set-op.test.mts +335 -0
- package/src/array/array-utils-slice-clamped.test.mts +113 -0
- package/src/array/array-utils-slicing.test.mts +316 -0
- package/src/array/array-utils-transformation.test.mts +790 -0
- package/src/array/array-utils-validation.test.mts +492 -0
- package/src/array/array-utils.mts +4000 -0
- package/src/array/array.test.mts +146 -0
- package/src/array/index.mts +2 -0
- package/src/array/tuple-utils.mts +519 -0
- package/src/array/tuple-utils.test.mts +518 -0
- package/src/collections/imap-mapped.mts +801 -0
- package/src/collections/imap-mapped.test.mts +860 -0
- package/src/collections/imap.mts +651 -0
- package/src/collections/imap.test.mts +932 -0
- package/src/collections/index.mts +6 -0
- package/src/collections/iset-mapped.mts +889 -0
- package/src/collections/iset-mapped.test.mts +1187 -0
- package/src/collections/iset.mts +682 -0
- package/src/collections/iset.test.mts +1084 -0
- package/src/collections/queue.mts +390 -0
- package/src/collections/queue.test.mts +282 -0
- package/src/collections/stack.mts +423 -0
- package/src/collections/stack.test.mts +225 -0
- package/src/expect-type.mts +206 -0
- package/src/functional/index.mts +4 -0
- package/src/functional/match.mts +300 -0
- package/src/functional/match.test.mts +177 -0
- package/src/functional/optional.mts +733 -0
- package/src/functional/optional.test.mts +619 -0
- package/src/functional/pipe.mts +212 -0
- package/src/functional/pipe.test.mts +85 -0
- package/src/functional/result.mts +1134 -0
- package/src/functional/result.test.mts +777 -0
- package/src/globals.d.mts +38 -0
- package/src/guard/has-key.mts +119 -0
- package/src/guard/has-key.test.mts +219 -0
- package/src/guard/index.mts +7 -0
- package/src/guard/is-non-empty-string.mts +108 -0
- package/src/guard/is-non-empty-string.test.mts +91 -0
- package/src/guard/is-non-null-object.mts +106 -0
- package/src/guard/is-non-null-object.test.mts +90 -0
- package/src/guard/is-primitive.mts +165 -0
- package/src/guard/is-primitive.test.mts +102 -0
- package/src/guard/is-record.mts +153 -0
- package/src/guard/is-record.test.mts +112 -0
- package/src/guard/is-type.mts +450 -0
- package/src/guard/is-type.test.mts +496 -0
- package/src/guard/key-is-in.mts +163 -0
- package/src/guard/key-is-in.test.mts +19 -0
- package/src/index.mts +10 -0
- package/src/iterator/index.mts +1 -0
- package/src/iterator/range.mts +120 -0
- package/src/iterator/range.test.mts +33 -0
- package/src/json/index.mts +1 -0
- package/src/json/json.mts +711 -0
- package/src/json/json.test.mts +628 -0
- package/src/number/branded-types/finite-number.mts +354 -0
- package/src/number/branded-types/finite-number.test.mts +135 -0
- package/src/number/branded-types/index.mts +26 -0
- package/src/number/branded-types/int.mts +278 -0
- package/src/number/branded-types/int.test.mts +140 -0
- package/src/number/branded-types/int16.mts +192 -0
- package/src/number/branded-types/int16.test.mts +170 -0
- package/src/number/branded-types/int32.mts +193 -0
- package/src/number/branded-types/int32.test.mts +170 -0
- package/src/number/branded-types/non-negative-finite-number.mts +223 -0
- package/src/number/branded-types/non-negative-finite-number.test.mts +188 -0
- package/src/number/branded-types/non-negative-int16.mts +187 -0
- package/src/number/branded-types/non-negative-int16.test.mts +201 -0
- package/src/number/branded-types/non-negative-int32.mts +187 -0
- package/src/number/branded-types/non-negative-int32.test.mts +204 -0
- package/src/number/branded-types/non-zero-finite-number.mts +229 -0
- package/src/number/branded-types/non-zero-finite-number.test.mts +198 -0
- package/src/number/branded-types/non-zero-int.mts +167 -0
- package/src/number/branded-types/non-zero-int.test.mts +177 -0
- package/src/number/branded-types/non-zero-int16.mts +196 -0
- package/src/number/branded-types/non-zero-int16.test.mts +195 -0
- package/src/number/branded-types/non-zero-int32.mts +196 -0
- package/src/number/branded-types/non-zero-int32.test.mts +197 -0
- package/src/number/branded-types/non-zero-safe-int.mts +196 -0
- package/src/number/branded-types/non-zero-safe-int.test.mts +232 -0
- package/src/number/branded-types/non-zero-uint16.mts +189 -0
- package/src/number/branded-types/non-zero-uint16.test.mts +199 -0
- package/src/number/branded-types/non-zero-uint32.mts +189 -0
- package/src/number/branded-types/non-zero-uint32.test.mts +199 -0
- package/src/number/branded-types/positive-finite-number.mts +241 -0
- package/src/number/branded-types/positive-finite-number.test.mts +204 -0
- package/src/number/branded-types/positive-int.mts +304 -0
- package/src/number/branded-types/positive-int.test.mts +176 -0
- package/src/number/branded-types/positive-int16.mts +188 -0
- package/src/number/branded-types/positive-int16.test.mts +197 -0
- package/src/number/branded-types/positive-int32.mts +188 -0
- package/src/number/branded-types/positive-int32.test.mts +197 -0
- package/src/number/branded-types/positive-safe-int.mts +187 -0
- package/src/number/branded-types/positive-safe-int.test.mts +210 -0
- package/src/number/branded-types/positive-uint16.mts +188 -0
- package/src/number/branded-types/positive-uint16.test.mts +203 -0
- package/src/number/branded-types/positive-uint32.mts +188 -0
- package/src/number/branded-types/positive-uint32.test.mts +203 -0
- package/src/number/branded-types/safe-int.mts +291 -0
- package/src/number/branded-types/safe-int.test.mts +170 -0
- package/src/number/branded-types/safe-uint.mts +187 -0
- package/src/number/branded-types/safe-uint.test.mts +176 -0
- package/src/number/branded-types/uint.mts +179 -0
- package/src/number/branded-types/uint.test.mts +158 -0
- package/src/number/branded-types/uint16.mts +186 -0
- package/src/number/branded-types/uint16.test.mts +170 -0
- package/src/number/branded-types/uint32.mts +218 -0
- package/src/number/branded-types/uint32.test.mts +170 -0
- package/src/number/enum/index.mts +2 -0
- package/src/number/enum/int8.mts +344 -0
- package/src/number/enum/int8.test.mts +180 -0
- package/src/number/enum/uint8.mts +293 -0
- package/src/number/enum/uint8.test.mts +164 -0
- package/src/number/index.mts +4 -0
- package/src/number/num.mts +604 -0
- package/src/number/num.test.mts +242 -0
- package/src/number/refined-number-utils.mts +566 -0
- package/src/object/index.mts +1 -0
- package/src/object/object.mts +447 -0
- package/src/object/object.test.mts +124 -0
- package/src/others/cast-mutable.mts +113 -0
- package/src/others/cast-readonly.mts +192 -0
- package/src/others/cast-readonly.test.mts +89 -0
- package/src/others/if-then.mts +98 -0
- package/src/others/if-then.test.mts +75 -0
- package/src/others/index.mts +7 -0
- package/src/others/map-nullable.mts +172 -0
- package/src/others/map-nullable.test.mts +297 -0
- package/src/others/memoize-function.mts +196 -0
- package/src/others/memoize-function.test.mts +168 -0
- package/src/others/tuple.mts +160 -0
- package/src/others/tuple.test.mts +11 -0
- package/src/others/unknown-to-string.mts +215 -0
- package/src/others/unknown-to-string.test.mts +114 -0
|
@@ -0,0 +1,801 @@
|
|
|
1
|
+
import { Optional, pipe } from '../functional/index.mjs';
|
|
2
|
+
import { asUint32 } from '../number/index.mjs';
|
|
3
|
+
import { tp } from '../others/index.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Interface for an immutable map with custom key mapping and O(1) lookup performance.
|
|
7
|
+
*
|
|
8
|
+
* IMapMapped allows you to use complex objects as keys by providing transformation functions
|
|
9
|
+
* that convert between your custom key type `K` and a primitive `MapSetKeyType` `KM` that can
|
|
10
|
+
* be efficiently stored in JavaScript's native Map. This enables high-performance operations
|
|
11
|
+
* on maps with complex keys while maintaining type safety and immutability.
|
|
12
|
+
*
|
|
13
|
+
* **Key Features:**
|
|
14
|
+
* - **Custom Key Types**: Use any type as keys by providing `toKey`/`fromKey` functions
|
|
15
|
+
* - **O(1) Performance**: Maintains O(1) average-case performance for core operations
|
|
16
|
+
* - **Immutable**: All operations return new instances, preserving immutability
|
|
17
|
+
* - **Type Safe**: Full TypeScript support with generic key/value types
|
|
18
|
+
*
|
|
19
|
+
* **Performance Characteristics:**
|
|
20
|
+
* - get/has/delete: O(1) average case (plus key transformation overhead)
|
|
21
|
+
* - set: O(1) average case (plus key transformation overhead)
|
|
22
|
+
* - map/filter operations: O(n)
|
|
23
|
+
* - Iteration: O(n) (plus key transformation overhead)
|
|
24
|
+
*
|
|
25
|
+
* @template K The type of the custom keys in the map.
|
|
26
|
+
* @template V The type of the values in the map.
|
|
27
|
+
* @template KM The type of the mapped primitive keys (string, number, etc.).
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* // Example with complex object keys
|
|
32
|
+
* type UserId = { department: string; employeeId: number };
|
|
33
|
+
*
|
|
34
|
+
* // Define transformation functions
|
|
35
|
+
* const userIdToKey = (id: UserId): string => `${id.department}:${id.employeeId}`;
|
|
36
|
+
* const keyToUserId = (key: string): UserId => {
|
|
37
|
+
* const [department, employeeId] = key.split(':');
|
|
38
|
+
* return { department, employeeId: Number(employeeId) };
|
|
39
|
+
* };
|
|
40
|
+
*
|
|
41
|
+
* declare const userMap: IMapMapped<UserId, UserProfile, string>;
|
|
42
|
+
*
|
|
43
|
+
* // All operations work with the complex key type
|
|
44
|
+
* const userId: UserId = { department: "engineering", employeeId: 123 };
|
|
45
|
+
* const hasUser = userMap.has(userId); // O(1)
|
|
46
|
+
* const profile = userMap.get(userId).unwrapOr(defaultProfile); // O(1)
|
|
47
|
+
* const updated = userMap.set(userId, newProfile); // O(1) - returns new IMapMapped
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
type IMapMappedInterface<K, V, KM extends MapSetKeyType> = Readonly<{
|
|
51
|
+
// Getting information
|
|
52
|
+
|
|
53
|
+
/** The number of elements in the map. */
|
|
54
|
+
size: SizeType.Arr;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Checks if a key exists in the map.
|
|
58
|
+
* @param key The key to check.
|
|
59
|
+
* @returns `true` if the key exists, `false` otherwise.
|
|
60
|
+
*/
|
|
61
|
+
has: (key: K) => boolean;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Retrieves the value associated with a key.
|
|
65
|
+
* @param key The key to retrieve.
|
|
66
|
+
* @returns The value associated with the key wrapped with `Optional.some`, or `Optional.none` if the key does not exist.
|
|
67
|
+
*/
|
|
68
|
+
get: (key: K) => Optional<V>;
|
|
69
|
+
|
|
70
|
+
// Reducing a value
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Checks if all elements in the map satisfy a predicate.
|
|
74
|
+
* @param predicate A function to test each key-value pair.
|
|
75
|
+
* @returns `true` if all elements satisfy the predicate, `false` otherwise.
|
|
76
|
+
*/
|
|
77
|
+
every: ((predicate: (value: V, key: K) => boolean) => boolean) &
|
|
78
|
+
/**
|
|
79
|
+
* Checks if all elements in the map satisfy a type predicate.
|
|
80
|
+
* Narrows the type of values in the map if the predicate returns true for all elements.
|
|
81
|
+
* @template W The narrowed type of the values.
|
|
82
|
+
* @param predicate A type predicate function.
|
|
83
|
+
* @returns `true` if all elements satisfy the predicate, `false` otherwise.
|
|
84
|
+
*/
|
|
85
|
+
(<W extends V>(
|
|
86
|
+
predicate: (value: V, key: K) => value is W,
|
|
87
|
+
) => this is IMapMapped<K, W, KM>);
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Checks if at least one element in the map satisfies a predicate.
|
|
91
|
+
* @param predicate A function to test each key-value pair.
|
|
92
|
+
* @returns `true` if at least one element satisfies the predicate, `false` otherwise.
|
|
93
|
+
*/
|
|
94
|
+
some: (predicate: (value: V, key: K) => boolean) => boolean;
|
|
95
|
+
|
|
96
|
+
// Mutation
|
|
97
|
+
/**
|
|
98
|
+
* Deletes a key-value pair from the map.
|
|
99
|
+
* @param key The key to delete.
|
|
100
|
+
* @returns A new IMapMapped instance without the specified key.
|
|
101
|
+
*/
|
|
102
|
+
delete: (key: K) => IMapMapped<K, V, KM>;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Sets a key-value pair in the map.
|
|
106
|
+
* @param key The key to set.
|
|
107
|
+
* @param value The value to associate with the key.
|
|
108
|
+
* @returns A new IMapMapped instance with the specified key-value pair.
|
|
109
|
+
*/
|
|
110
|
+
set: (key: K, value: V) => IMapMapped<K, V, KM>;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Updates the value associated with a key using an updater function.
|
|
114
|
+
* @param key The key whose value to update.
|
|
115
|
+
* @param updater A function that takes the current value and returns the new value.
|
|
116
|
+
* @returns A new IMapMapped instance with the updated value.
|
|
117
|
+
*/
|
|
118
|
+
update: (key: K, updater: (value: V) => V) => IMapMapped<K, V, KM>;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Applies a series of mutations to the map.
|
|
122
|
+
* @param actions An array of mutation actions (delete, set, or update).
|
|
123
|
+
* @returns A new IMapMapped instance with all mutations applied.
|
|
124
|
+
*/
|
|
125
|
+
withMutations: (
|
|
126
|
+
actions: readonly Readonly<
|
|
127
|
+
| { type: 'delete'; key: K }
|
|
128
|
+
| { type: 'set'; key: K; value: V }
|
|
129
|
+
| { type: 'update'; key: K; updater: (value: V) => V }
|
|
130
|
+
>[],
|
|
131
|
+
) => IMapMapped<K, V, KM>;
|
|
132
|
+
|
|
133
|
+
// Sequence algorithms
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Maps the values of the map to new values.
|
|
137
|
+
* @template V2 The type of the new values.
|
|
138
|
+
* @param mapFn A function that maps a value and key to a new value.
|
|
139
|
+
* @returns A new IMapMapped instance with mapped values.
|
|
140
|
+
*/
|
|
141
|
+
map: <V2>(mapFn: (value: V, key: K) => V2) => IMapMapped<K, V2, KM>;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Maps the keys of the map to new keys.
|
|
145
|
+
* Note: The key type cannot be changed because `toKey` and `fromKey` would become unusable.
|
|
146
|
+
* @param mapFn A function that maps a key to a new key of the same type.
|
|
147
|
+
* @returns A new IMapMapped instance with mapped keys.
|
|
148
|
+
*/
|
|
149
|
+
mapKeys: (mapFn: (key: K) => K) => IMapMapped<K, V, KM>;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Maps the entries (key-value pairs) of the map to new entries.
|
|
153
|
+
* @template V2 The type of the new values in the entries.
|
|
154
|
+
* @param mapFn A function that maps an entry to a new entry (key must remain the same type).
|
|
155
|
+
* @returns A new IMapMapped instance with mapped entries.
|
|
156
|
+
*/
|
|
157
|
+
mapEntries: <V2>(
|
|
158
|
+
mapFn: (entry: readonly [K, V]) => readonly [K, V2],
|
|
159
|
+
) => IMapMapped<K, V2, KM>;
|
|
160
|
+
|
|
161
|
+
// Side effects
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Executes a callback function for each key-value pair in the map.
|
|
165
|
+
* @param callbackfn A function to execute for each element.
|
|
166
|
+
*/
|
|
167
|
+
forEach: (callbackfn: (value: V, key: K) => void) => void;
|
|
168
|
+
|
|
169
|
+
// Iterators
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Returns an iterator for the keys in the map.
|
|
173
|
+
* @returns An iterable iterator of keys.
|
|
174
|
+
*/
|
|
175
|
+
keys: () => IterableIterator<K>;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Returns an iterator for the values in the map.
|
|
179
|
+
* @returns An iterable iterator of values.
|
|
180
|
+
*/
|
|
181
|
+
values: () => IterableIterator<V>;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Returns an iterator for the entries (key-value pairs) in the map.
|
|
185
|
+
* @returns An iterable iterator of entries.
|
|
186
|
+
*/
|
|
187
|
+
entries: () => IterableIterator<readonly [K, V]>;
|
|
188
|
+
|
|
189
|
+
// Conversion
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Converts the keys of the map to an array.
|
|
193
|
+
* @returns A readonly array of keys.
|
|
194
|
+
*/
|
|
195
|
+
toKeysArray: () => readonly K[];
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Converts the values of the map to an array.
|
|
199
|
+
* @returns A readonly array of values.
|
|
200
|
+
*/
|
|
201
|
+
toValuesArray: () => readonly V[];
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Converts the entries (key-value pairs) of the map to an array.
|
|
205
|
+
* @returns A readonly array of entries.
|
|
206
|
+
*/
|
|
207
|
+
toEntriesArray: () => readonly (readonly [K, V])[];
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Converts the map to an array of entries (key-value pairs).
|
|
211
|
+
* Alias for `toEntriesArray`.
|
|
212
|
+
* @returns A readonly array of entries.
|
|
213
|
+
*/
|
|
214
|
+
toArray: () => readonly (readonly [K, V])[];
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Returns the underlying readonly JavaScript Map.
|
|
218
|
+
* @returns The raw ReadonlyMap instance.
|
|
219
|
+
*/
|
|
220
|
+
toRawMap: () => ReadonlyMap<KM, V>;
|
|
221
|
+
}>;
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Represents an immutable map with custom key transformation and high-performance operations.
|
|
225
|
+
*
|
|
226
|
+
* IMapMapped is a specialized persistent data structure that enables using complex objects as map keys
|
|
227
|
+
* while maintaining the performance benefits of JavaScript's native Map. It achieves this by requiring
|
|
228
|
+
* bidirectional transformation functions that convert between your custom key type and a primitive type
|
|
229
|
+
* that can be efficiently stored and compared.
|
|
230
|
+
*
|
|
231
|
+
* **Key Features:**
|
|
232
|
+
* - **Complex Keys**: Use objects, arrays, or any custom type as map keys
|
|
233
|
+
* - **High Performance**: O(1) operations through efficient key transformation
|
|
234
|
+
* - **Immutable**: All mutation operations return new instances
|
|
235
|
+
* - **Type Safe**: Full TypeScript support with compile-time key/value type checking
|
|
236
|
+
* - **Bidirectional**: Maintains ability to reconstruct original keys from mapped keys
|
|
237
|
+
*
|
|
238
|
+
* **Use Cases:**
|
|
239
|
+
* - Maps with composite keys (e.g., coordinates, user IDs with metadata)
|
|
240
|
+
* - Caching with complex cache keys
|
|
241
|
+
* - State management where entities have multi-part identifiers
|
|
242
|
+
* - Performance-critical maps with non-primitive keys
|
|
243
|
+
*
|
|
244
|
+
* @template K The type of the custom keys in the map.
|
|
245
|
+
* @template V The type of the values in the map.
|
|
246
|
+
* @template KM The type of the mapped primitive keys (string, number, etc.).
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```typescript
|
|
250
|
+
* // Example: Product catalog with composite keys
|
|
251
|
+
* type ProductKey = { brand: string; model: string; year: number };
|
|
252
|
+
* type Product = { name: string; price: number; inStock: boolean };
|
|
253
|
+
*
|
|
254
|
+
* // Define bidirectional transformation functions
|
|
255
|
+
* const productKeyToString = (key: ProductKey): string =>
|
|
256
|
+
* `${key.brand}|${key.model}|${key.year}`;
|
|
257
|
+
*
|
|
258
|
+
* const stringToProductKey = (str: string): ProductKey => {
|
|
259
|
+
* const [brand, model, yearStr] = str.split('|');
|
|
260
|
+
* return { brand, model, year: Number(yearStr) };
|
|
261
|
+
* };
|
|
262
|
+
*
|
|
263
|
+
* // Create a map with complex keys
|
|
264
|
+
* let catalog = IMapMapped.create<ProductKey, Product, string>(
|
|
265
|
+
* [],
|
|
266
|
+
* productKeyToString,
|
|
267
|
+
* stringToProductKey
|
|
268
|
+
* );
|
|
269
|
+
*
|
|
270
|
+
* // Use complex objects as keys naturally
|
|
271
|
+
* const toyotaCamry2023: ProductKey = { brand: "Toyota", model: "Camry", year: 2023 };
|
|
272
|
+
* const hondaAccord2022: ProductKey = { brand: "Honda", model: "Accord", year: 2022 };
|
|
273
|
+
*
|
|
274
|
+
* catalog = catalog
|
|
275
|
+
* .set(toyotaCamry2023, { name: "Toyota Camry 2023", price: 28000, inStock: true })
|
|
276
|
+
* .set(hondaAccord2022, { name: "Honda Accord 2022", price: 26500, inStock: false });
|
|
277
|
+
*
|
|
278
|
+
* // All operations work with the original key type
|
|
279
|
+
* console.log(catalog.get(toyotaCamry2023).unwrapOr(notFound).name);
|
|
280
|
+
* // Output: "Toyota Camry 2023"
|
|
281
|
+
*
|
|
282
|
+
* console.log(catalog.has(hondaAccord2022)); // Output: true
|
|
283
|
+
* console.log(catalog.size); // Output: 2
|
|
284
|
+
*
|
|
285
|
+
* // Iteration preserves original key types
|
|
286
|
+
* for (const [productKey, product] of catalog) {
|
|
287
|
+
* console.log(`${productKey.brand} ${productKey.model} (${productKey.year}): $${product.price}`);
|
|
288
|
+
* }
|
|
289
|
+
* // Output:
|
|
290
|
+
* // Toyota Camry (2023): $28000
|
|
291
|
+
* // Honda Accord (2022): $26500
|
|
292
|
+
*
|
|
293
|
+
* // Functional transformations work seamlessly
|
|
294
|
+
* const discountedCatalog = catalog.map((product, key) => ({
|
|
295
|
+
* ...product,
|
|
296
|
+
* price: Math.round(product.price * 0.9) // 10% discount
|
|
297
|
+
* }));
|
|
298
|
+
* ```
|
|
299
|
+
*/
|
|
300
|
+
export type IMapMapped<K, V, KM extends MapSetKeyType> = Iterable<
|
|
301
|
+
readonly [K, V]
|
|
302
|
+
> &
|
|
303
|
+
IMapMappedInterface<K, V, KM>;
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Provides utility functions for IMapMapped.
|
|
307
|
+
*/
|
|
308
|
+
export namespace IMapMapped {
|
|
309
|
+
/**
|
|
310
|
+
* Creates a new IMapMapped instance with custom key transformation functions.
|
|
311
|
+
*
|
|
312
|
+
* This factory function creates an immutable map that can use complex objects as keys
|
|
313
|
+
* by providing bidirectional transformation functions. The `toKey` function converts
|
|
314
|
+
* your custom key type to a primitive type that can be efficiently stored, while
|
|
315
|
+
* `fromKey` reconstructs the original key type for iteration and access.
|
|
316
|
+
*
|
|
317
|
+
* **Performance:** O(n) where n is the number of entries in the iterable.
|
|
318
|
+
*
|
|
319
|
+
* @template K The type of the custom keys.
|
|
320
|
+
* @template V The type of the values.
|
|
321
|
+
* @template KM The type of the mapped primitive keys.
|
|
322
|
+
* @param iterable An iterable of key-value pairs using the custom key type.
|
|
323
|
+
* @param toKey A function that converts a custom key `K` to a primitive key `KM`.
|
|
324
|
+
* This function must be deterministic and produce unique values for unique keys.
|
|
325
|
+
* @param fromKey A function that converts a primitive key `KM` back to the custom key `K`.
|
|
326
|
+
* This should be the inverse of `toKey`.
|
|
327
|
+
* @returns A new IMapMapped instance containing all entries from the iterable.
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* ```typescript
|
|
331
|
+
* // Example 1: Geographic coordinates as keys
|
|
332
|
+
* type Coordinate = { lat: number; lng: number };
|
|
333
|
+
* type LocationInfo = { name: string; population: number };
|
|
334
|
+
*
|
|
335
|
+
* const coordToString = (coord: Coordinate): string => `${coord.lat},${coord.lng}`;
|
|
336
|
+
* const stringToCoord = (str: string): Coordinate => {
|
|
337
|
+
* const [lat, lng] = str.split(',').map(Number);
|
|
338
|
+
* return { lat, lng };
|
|
339
|
+
* };
|
|
340
|
+
*
|
|
341
|
+
* const locationMap = IMapMapped.create<Coordinate, LocationInfo, string>(
|
|
342
|
+
* [
|
|
343
|
+
* [{ lat: 40.7128, lng: -74.0060 }, { name: "New York", population: 8000000 }],
|
|
344
|
+
* [{ lat: 34.0522, lng: -118.2437 }, { name: "Los Angeles", population: 4000000 }]
|
|
345
|
+
* ],
|
|
346
|
+
* coordToString,
|
|
347
|
+
* stringToCoord
|
|
348
|
+
* );
|
|
349
|
+
*
|
|
350
|
+
* const nyCoord = { lat: 40.7128, lng: -74.0060 };
|
|
351
|
+
* console.log(locationMap.get(nyCoord).unwrap().name); // Output: "New York"
|
|
352
|
+
*
|
|
353
|
+
* // Example 2: Multi-part business keys
|
|
354
|
+
* type OrderId = { customerId: string; year: number; orderNumber: number };
|
|
355
|
+
*
|
|
356
|
+
* const orderIdToKey = (id: OrderId): string =>
|
|
357
|
+
* `${id.customerId}:${id.year}:${id.orderNumber}`;
|
|
358
|
+
*
|
|
359
|
+
* const keyToOrderId = (key: string): OrderId => {
|
|
360
|
+
* const [customerId, yearStr, orderNumStr] = key.split(':');
|
|
361
|
+
* return {
|
|
362
|
+
* customerId,
|
|
363
|
+
* year: Number(yearStr),
|
|
364
|
+
* orderNumber: Number(orderNumStr)
|
|
365
|
+
* };
|
|
366
|
+
* };
|
|
367
|
+
*
|
|
368
|
+
* const orderMap = IMapMapped.create<OrderId, Order, string>(
|
|
369
|
+
* [],
|
|
370
|
+
* orderIdToKey,
|
|
371
|
+
* keyToOrderId
|
|
372
|
+
* );
|
|
373
|
+
*
|
|
374
|
+
* // Example 3: Simple case with string keys (identity transformation)
|
|
375
|
+
* const simpleMap = IMapMapped.create<string, number, string>(
|
|
376
|
+
* [["key1", 100], ["key2", 200]],
|
|
377
|
+
* (s) => s, // identity function
|
|
378
|
+
* (s) => s // identity function
|
|
379
|
+
* );
|
|
380
|
+
*
|
|
381
|
+
* // Example 4: From existing data structures
|
|
382
|
+
* const existingEntries = new Map([
|
|
383
|
+
* [{ id: 1, type: "user" }, { name: "Alice", active: true }],
|
|
384
|
+
* [{ id: 2, type: "user" }, { name: "Bob", active: false }]
|
|
385
|
+
* ]);
|
|
386
|
+
*
|
|
387
|
+
* type EntityKey = { id: number; type: string };
|
|
388
|
+
* const entityKeyToString = (key: EntityKey): string => `${key.type}_${key.id}`;
|
|
389
|
+
* const stringToEntityKey = (str: string): EntityKey => {
|
|
390
|
+
* const [type, idStr] = str.split('_');
|
|
391
|
+
* return { type, id: Number(idStr) };
|
|
392
|
+
* };
|
|
393
|
+
*
|
|
394
|
+
* const entityMap = IMapMapped.create<EntityKey, Entity, string>(
|
|
395
|
+
* existingEntries,
|
|
396
|
+
* entityKeyToString,
|
|
397
|
+
* stringToEntityKey
|
|
398
|
+
* );
|
|
399
|
+
* ```
|
|
400
|
+
*/
|
|
401
|
+
export const create = <K, V, KM extends MapSetKeyType>(
|
|
402
|
+
iterable: Iterable<readonly [K, V]>,
|
|
403
|
+
toKey: (a: K) => KM,
|
|
404
|
+
fromKey: (k: KM) => K,
|
|
405
|
+
): IMapMapped<K, V, KM> =>
|
|
406
|
+
new IMapMappedClass<K, V, KM>(iterable, toKey, fromKey);
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Checks if two IMapMapped instances are structurally equal.
|
|
410
|
+
*
|
|
411
|
+
* Two IMapMapped instances are considered equal if they have the same size and contain
|
|
412
|
+
* exactly the same key-value pairs. The comparison is performed on the underlying mapped
|
|
413
|
+
* keys and values, so the transformation functions themselves don't need to be identical.
|
|
414
|
+
* Values are compared using JavaScript's `===` operator.
|
|
415
|
+
*
|
|
416
|
+
* **Performance:** O(n) where n is the size of the smaller map.
|
|
417
|
+
*
|
|
418
|
+
* @template K The type of the custom keys.
|
|
419
|
+
* @template V The type of the values.
|
|
420
|
+
* @template KM The type of the mapped primitive keys.
|
|
421
|
+
* @param a The first IMapMapped instance to compare.
|
|
422
|
+
* @param b The second IMapMapped instance to compare.
|
|
423
|
+
* @returns `true` if the maps contain exactly the same key-value pairs, `false` otherwise.
|
|
424
|
+
*
|
|
425
|
+
* @example
|
|
426
|
+
* ```typescript
|
|
427
|
+
* // Example with coordinate keys
|
|
428
|
+
* type Point = { x: number; y: number };
|
|
429
|
+
* const pointToString = (p: Point): string => `${p.x},${p.y}`;
|
|
430
|
+
* const stringToPoint = (s: string): Point => {
|
|
431
|
+
* const [x, y] = s.split(',').map(Number);
|
|
432
|
+
* return { x, y };
|
|
433
|
+
* };
|
|
434
|
+
*
|
|
435
|
+
* const map1 = IMapMapped.create<Point, string, string>(
|
|
436
|
+
* [[{ x: 1, y: 2 }, "point1"], [{ x: 3, y: 4 }, "point2"]],
|
|
437
|
+
* pointToString,
|
|
438
|
+
* stringToPoint
|
|
439
|
+
* );
|
|
440
|
+
*
|
|
441
|
+
* const map2 = IMapMapped.create<Point, string, string>(
|
|
442
|
+
* [[{ x: 1, y: 2 }, "point1"], [{ x: 3, y: 4 }, "point2"]], // Same content
|
|
443
|
+
* pointToString,
|
|
444
|
+
* stringToPoint
|
|
445
|
+
* );
|
|
446
|
+
*
|
|
447
|
+
* const map3 = IMapMapped.create<Point, string, string>(
|
|
448
|
+
* [[{ x: 1, y: 2 }, "point1"], [{ x: 3, y: 4 }, "different"]], // Different value
|
|
449
|
+
* pointToString,
|
|
450
|
+
* stringToPoint
|
|
451
|
+
* );
|
|
452
|
+
*
|
|
453
|
+
* console.log(IMapMapped.equal(map1, map2)); // true
|
|
454
|
+
* console.log(IMapMapped.equal(map1, map3)); // false (different value)
|
|
455
|
+
*
|
|
456
|
+
* // Order doesn't matter for equality
|
|
457
|
+
* const map4 = IMapMapped.create<Point, string, string>(
|
|
458
|
+
* [[{ x: 3, y: 4 }, "point2"], [{ x: 1, y: 2 }, "point1"]], // Different order
|
|
459
|
+
* pointToString,
|
|
460
|
+
* stringToPoint
|
|
461
|
+
* );
|
|
462
|
+
*
|
|
463
|
+
* console.log(IMapMapped.equal(map1, map4)); // true
|
|
464
|
+
*
|
|
465
|
+
* // Different transformation functions but same logical content
|
|
466
|
+
* const alternativePointToString = (p: Point): string => `(${p.x},${p.y})`; // Different format
|
|
467
|
+
* const alternativeStringToPoint = (s: string): Point => {
|
|
468
|
+
* const match = s.match(/\((\d+),(\d+)\)/);
|
|
469
|
+
* return { x: Number(match![1]), y: Number(match![2]) };
|
|
470
|
+
* };
|
|
471
|
+
*
|
|
472
|
+
* const map5 = IMapMapped.create<Point, string, string>(
|
|
473
|
+
* [[{ x: 1, y: 2 }, "point1"], [{ x: 3, y: 4 }, "point2"]],
|
|
474
|
+
* alternativePointToString,
|
|
475
|
+
* alternativeStringToPoint
|
|
476
|
+
* );
|
|
477
|
+
*
|
|
478
|
+
* // This would be false because the underlying mapped keys are different
|
|
479
|
+
* // even though the logical content is the same
|
|
480
|
+
* console.log(IMapMapped.equal(map1, map5)); // false
|
|
481
|
+
*
|
|
482
|
+
* // Empty maps
|
|
483
|
+
* const empty1 = IMapMapped.create<Point, string, string>([], pointToString, stringToPoint);
|
|
484
|
+
* const empty2 = IMapMapped.create<Point, string, string>([], pointToString, stringToPoint);
|
|
485
|
+
* console.log(IMapMapped.equal(empty1, empty2)); // true
|
|
486
|
+
* ```
|
|
487
|
+
*/
|
|
488
|
+
export const equal = <K, V, KM extends MapSetKeyType>(
|
|
489
|
+
a: IMapMapped<K, V, KM>,
|
|
490
|
+
b: IMapMapped<K, V, KM>,
|
|
491
|
+
): boolean => a.size === b.size && a.every((v, k) => b.get(k) === v);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Internal class implementation for IMapMapped providing immutable map operations with key transformation.
|
|
496
|
+
*
|
|
497
|
+
* This class implements the IMapMapped interface by maintaining a JavaScript Map with primitive keys
|
|
498
|
+
* internally while exposing an API that works with custom key types. The transformation between
|
|
499
|
+
* custom and primitive keys is handled transparently through the provided `toKey` and `fromKey` functions.
|
|
500
|
+
*
|
|
501
|
+
* **Implementation Details:**
|
|
502
|
+
* - Uses ReadonlyMap<KM, V> internally where KM is the primitive key type
|
|
503
|
+
* - Stores transformation functions for bidirectional key conversion
|
|
504
|
+
* - Implements copy-on-write semantics for efficiency
|
|
505
|
+
* - Provides optional debug messaging for development
|
|
506
|
+
*
|
|
507
|
+
* @template K The type of the custom keys.
|
|
508
|
+
* @template V The type of the values.
|
|
509
|
+
* @template KM The type of the mapped primitive keys.
|
|
510
|
+
* @implements IMapMapped
|
|
511
|
+
* @implements Iterable
|
|
512
|
+
* @internal This class should not be used directly. Use IMapMapped.create() instead.
|
|
513
|
+
*/
|
|
514
|
+
class IMapMappedClass<K, V, KM extends MapSetKeyType>
|
|
515
|
+
implements IMapMapped<K, V, KM>, Iterable<readonly [K, V]>
|
|
516
|
+
{
|
|
517
|
+
readonly #map: ReadonlyMap<KM, V>;
|
|
518
|
+
readonly #toKey: (a: K) => KM;
|
|
519
|
+
readonly #fromKey: (k: KM) => K;
|
|
520
|
+
readonly #showNotFoundMessage: boolean;
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* Constructs an IMapMappedClass instance with custom key transformation.
|
|
524
|
+
*
|
|
525
|
+
* @param iterable An iterable of key-value pairs using the custom key type K.
|
|
526
|
+
* @param toKey A function that converts a custom key K to a primitive key KM.
|
|
527
|
+
* Must be deterministic and produce unique values for unique keys.
|
|
528
|
+
* @param fromKey A function that converts a primitive key KM back to the custom key K.
|
|
529
|
+
* Should be the inverse of the toKey function.
|
|
530
|
+
* @param showNotFoundMessage Whether to log warning messages when operations
|
|
531
|
+
* are performed on non-existent keys. Useful for debugging.
|
|
532
|
+
* Defaults to false for production use.
|
|
533
|
+
* @internal Use IMapMapped.create() instead of calling this constructor directly.
|
|
534
|
+
*/
|
|
535
|
+
constructor(
|
|
536
|
+
iterable: Iterable<readonly [K, V]>,
|
|
537
|
+
toKey: (a: K) => KM,
|
|
538
|
+
fromKey: (k: KM) => K,
|
|
539
|
+
showNotFoundMessage: boolean = false,
|
|
540
|
+
) {
|
|
541
|
+
this.#map = new Map(Array.from(iterable, ([k, v]) => [toKey(k), v]));
|
|
542
|
+
this.#toKey = toKey;
|
|
543
|
+
this.#fromKey = fromKey;
|
|
544
|
+
this.#showNotFoundMessage = showNotFoundMessage;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/** @inheritdoc */
|
|
548
|
+
get size(): SizeType.Arr {
|
|
549
|
+
return asUint32(this.#map.size);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/** @inheritdoc */
|
|
553
|
+
has(key: K): boolean {
|
|
554
|
+
return this.#map.has(this.#toKey(key));
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/** @inheritdoc */
|
|
558
|
+
get(key: K): Optional<V> {
|
|
559
|
+
if (!this.has(key)) return Optional.none;
|
|
560
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
561
|
+
return Optional.some(this.#map.get(this.#toKey(key))!);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/** @inheritdoc */
|
|
565
|
+
every<W extends V>(
|
|
566
|
+
predicate: (value: V, key: K) => value is W,
|
|
567
|
+
): this is IMapMapped<K, W, KM>;
|
|
568
|
+
/** @inheritdoc */
|
|
569
|
+
every(predicate: (value: V, key: K) => boolean): boolean;
|
|
570
|
+
/** @inheritdoc */
|
|
571
|
+
every(predicate: (value: V, key: K) => boolean): boolean {
|
|
572
|
+
for (const [k, v] of this.entries()) {
|
|
573
|
+
if (!predicate(v, k)) return false;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return true;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/** @inheritdoc */
|
|
580
|
+
some(predicate: (value: V, key: K) => boolean): boolean {
|
|
581
|
+
for (const [k, v] of this.entries()) {
|
|
582
|
+
if (predicate(v, k)) return true;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/** @inheritdoc */
|
|
589
|
+
delete(key: K): IMapMapped<K, V, KM> {
|
|
590
|
+
if (!this.has(key)) {
|
|
591
|
+
if (this.#showNotFoundMessage) {
|
|
592
|
+
console.warn(
|
|
593
|
+
`IMapMapped.delete: key not found: ${String(this.#toKey(key))}`,
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
return this;
|
|
597
|
+
}
|
|
598
|
+
const keyMapped = this.#toKey(key);
|
|
599
|
+
|
|
600
|
+
return IMapMapped.create(
|
|
601
|
+
Array.from(this.#map)
|
|
602
|
+
.filter(([km]) => !Object.is(km, keyMapped))
|
|
603
|
+
.map(([km, v]) => tp(this.#fromKey(km), v)),
|
|
604
|
+
this.#toKey,
|
|
605
|
+
this.#fromKey,
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/** @inheritdoc */
|
|
610
|
+
set(key: K, value: V): IMapMapped<K, V, KM> {
|
|
611
|
+
if (value === this.get(key)) return this; // has no changes
|
|
612
|
+
const keyMapped = this.#toKey(key);
|
|
613
|
+
|
|
614
|
+
if (!this.has(key)) {
|
|
615
|
+
return IMapMapped.create(
|
|
616
|
+
[...this.#map, tp(keyMapped, value)].map(([km, v]) =>
|
|
617
|
+
tp(this.#fromKey(km), v),
|
|
618
|
+
),
|
|
619
|
+
this.#toKey,
|
|
620
|
+
this.#fromKey,
|
|
621
|
+
);
|
|
622
|
+
} else {
|
|
623
|
+
return IMapMapped.create(
|
|
624
|
+
Array.from(this.#map, ([km, v]) =>
|
|
625
|
+
tp(this.#fromKey(km), Object.is(km, keyMapped) ? value : v),
|
|
626
|
+
),
|
|
627
|
+
this.#toKey,
|
|
628
|
+
this.#fromKey,
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/** @inheritdoc */
|
|
634
|
+
update(key: K, updater: (value: V) => V): IMapMapped<K, V, KM> {
|
|
635
|
+
const curr = this.get(key);
|
|
636
|
+
|
|
637
|
+
if (Optional.isNone(curr)) {
|
|
638
|
+
if (this.#showNotFoundMessage) {
|
|
639
|
+
console.warn(
|
|
640
|
+
`IMapMapped.update: key not found: ${String(this.#toKey(key))}`,
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
return this;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
const keyMapped = this.#toKey(key);
|
|
647
|
+
|
|
648
|
+
return IMapMapped.create(
|
|
649
|
+
Array.from(
|
|
650
|
+
this.#map.entries(),
|
|
651
|
+
(keyValue) =>
|
|
652
|
+
pipe(keyValue)
|
|
653
|
+
.map(([km, v]) =>
|
|
654
|
+
tp(km, Object.is(km, keyMapped) ? updater(curr.value) : v),
|
|
655
|
+
)
|
|
656
|
+
.map(([km, v]) => tp(this.#fromKey(km), v)).value,
|
|
657
|
+
),
|
|
658
|
+
this.#toKey,
|
|
659
|
+
this.#fromKey,
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/** @inheritdoc */
|
|
664
|
+
withMutations(
|
|
665
|
+
actions: readonly Readonly<
|
|
666
|
+
| { type: 'delete'; key: K }
|
|
667
|
+
| { type: 'set'; key: K; value: V }
|
|
668
|
+
| { type: 'update'; key: K; updater: (value: V) => V }
|
|
669
|
+
>[],
|
|
670
|
+
): IMapMapped<K, V, KM> {
|
|
671
|
+
const mut_result = new Map<KM, V>(this.#map);
|
|
672
|
+
|
|
673
|
+
for (const action of actions) {
|
|
674
|
+
const key = this.#toKey(action.key);
|
|
675
|
+
|
|
676
|
+
switch (action.type) {
|
|
677
|
+
case 'delete':
|
|
678
|
+
mut_result.delete(key);
|
|
679
|
+
break;
|
|
680
|
+
|
|
681
|
+
case 'set':
|
|
682
|
+
mut_result.set(key, action.value);
|
|
683
|
+
break;
|
|
684
|
+
|
|
685
|
+
case 'update': {
|
|
686
|
+
const curr = mut_result.get(key);
|
|
687
|
+
|
|
688
|
+
if (!mut_result.has(key) || curr === undefined) {
|
|
689
|
+
if (this.#showNotFoundMessage) {
|
|
690
|
+
console.warn(
|
|
691
|
+
`IMapMapped.withMutations::update: key not found: ${String(key)}`,
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
mut_result.set(key, action.updater(curr));
|
|
698
|
+
|
|
699
|
+
break;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
return IMapMapped.create<K, V, KM>(
|
|
705
|
+
Array.from(mut_result, ([k, v]) => [this.#fromKey(k), v]),
|
|
706
|
+
this.#toKey,
|
|
707
|
+
this.#fromKey,
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/** @inheritdoc */
|
|
712
|
+
map<V2>(mapFn: (value: V, key: K) => V2): IMapMapped<K, V2, KM> {
|
|
713
|
+
return IMapMapped.create(
|
|
714
|
+
this.toArray().map(([k, v]) => tp(k, mapFn(v, k))),
|
|
715
|
+
this.#toKey,
|
|
716
|
+
this.#fromKey,
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/** @inheritdoc */
|
|
721
|
+
mapKeys(mapFn: (key: K) => K): IMapMapped<K, V, KM> {
|
|
722
|
+
return IMapMapped.create(
|
|
723
|
+
this.toArray().map(([k, v]) => tp(mapFn(k), v)),
|
|
724
|
+
this.#toKey,
|
|
725
|
+
this.#fromKey,
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/** @inheritdoc */
|
|
730
|
+
mapEntries<V2>(
|
|
731
|
+
mapFn: (entry: readonly [K, V]) => readonly [K, V2],
|
|
732
|
+
): IMapMapped<K, V2, KM> {
|
|
733
|
+
return IMapMapped.create(
|
|
734
|
+
this.toArray().map(mapFn),
|
|
735
|
+
this.#toKey,
|
|
736
|
+
this.#fromKey,
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/** @inheritdoc */
|
|
741
|
+
forEach(callbackfn: (value: V, key: K) => void): void {
|
|
742
|
+
for (const [km, value] of this.#map.entries()) {
|
|
743
|
+
callbackfn(value, this.#fromKey(km));
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* @inheritdoc
|
|
749
|
+
*/
|
|
750
|
+
*[Symbol.iterator](): Iterator<readonly [K, V]> {
|
|
751
|
+
for (const e of this.entries()) {
|
|
752
|
+
yield e;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/** @inheritdoc */
|
|
757
|
+
*keys(): IterableIterator<K> {
|
|
758
|
+
for (const km of this.#map.keys()) {
|
|
759
|
+
yield this.#fromKey(km);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/** @inheritdoc */
|
|
764
|
+
*values(): IterableIterator<V> {
|
|
765
|
+
for (const v of this.#map.values()) {
|
|
766
|
+
yield v;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/** @inheritdoc */
|
|
771
|
+
*entries(): IterableIterator<readonly [K, V]> {
|
|
772
|
+
for (const [km, v] of this.#map.entries()) {
|
|
773
|
+
yield [this.#fromKey(km), v];
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/** @inheritdoc */
|
|
778
|
+
toKeysArray(): readonly K[] {
|
|
779
|
+
return Array.from(this.keys());
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/** @inheritdoc */
|
|
783
|
+
toValuesArray(): readonly V[] {
|
|
784
|
+
return Array.from(this.values());
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/** @inheritdoc */
|
|
788
|
+
toEntriesArray(): readonly (readonly [K, V])[] {
|
|
789
|
+
return Array.from(this.entries());
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/** @inheritdoc */
|
|
793
|
+
toArray(): readonly (readonly [K, V])[] {
|
|
794
|
+
return Array.from(this.entries());
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/** @inheritdoc */
|
|
798
|
+
toRawMap(): ReadonlyMap<KM, V> {
|
|
799
|
+
return this.#map;
|
|
800
|
+
}
|
|
801
|
+
}
|