stack-typed 2.0.5 → 2.1.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 (101) hide show
  1. package/dist/data-structures/base/iterable-element-base.d.ts +186 -83
  2. package/dist/data-structures/base/iterable-element-base.js +149 -107
  3. package/dist/data-structures/base/iterable-entry-base.d.ts +95 -119
  4. package/dist/data-structures/base/iterable-entry-base.js +59 -116
  5. package/dist/data-structures/base/linear-base.d.ts +250 -192
  6. package/dist/data-structures/base/linear-base.js +137 -274
  7. package/dist/data-structures/binary-tree/avl-tree-counter.d.ts +126 -158
  8. package/dist/data-structures/binary-tree/avl-tree-counter.js +171 -205
  9. package/dist/data-structures/binary-tree/avl-tree-multi-map.d.ts +100 -69
  10. package/dist/data-structures/binary-tree/avl-tree-multi-map.js +135 -87
  11. package/dist/data-structures/binary-tree/avl-tree.d.ts +138 -149
  12. package/dist/data-structures/binary-tree/avl-tree.js +208 -195
  13. package/dist/data-structures/binary-tree/binary-tree.d.ts +476 -632
  14. package/dist/data-structures/binary-tree/binary-tree.js +598 -869
  15. package/dist/data-structures/binary-tree/bst.d.ts +258 -306
  16. package/dist/data-structures/binary-tree/bst.js +505 -481
  17. package/dist/data-structures/binary-tree/red-black-tree.d.ts +107 -179
  18. package/dist/data-structures/binary-tree/red-black-tree.js +114 -209
  19. package/dist/data-structures/binary-tree/tree-counter.d.ts +132 -154
  20. package/dist/data-structures/binary-tree/tree-counter.js +172 -203
  21. package/dist/data-structures/binary-tree/tree-multi-map.d.ts +72 -69
  22. package/dist/data-structures/binary-tree/tree-multi-map.js +105 -85
  23. package/dist/data-structures/graph/abstract-graph.d.ts +238 -233
  24. package/dist/data-structures/graph/abstract-graph.js +267 -237
  25. package/dist/data-structures/graph/directed-graph.d.ts +108 -224
  26. package/dist/data-structures/graph/directed-graph.js +146 -233
  27. package/dist/data-structures/graph/map-graph.d.ts +49 -55
  28. package/dist/data-structures/graph/map-graph.js +56 -59
  29. package/dist/data-structures/graph/undirected-graph.d.ts +103 -146
  30. package/dist/data-structures/graph/undirected-graph.js +129 -149
  31. package/dist/data-structures/hash/hash-map.d.ts +164 -338
  32. package/dist/data-structures/hash/hash-map.js +270 -457
  33. package/dist/data-structures/heap/heap.d.ts +214 -289
  34. package/dist/data-structures/heap/heap.js +340 -349
  35. package/dist/data-structures/heap/max-heap.d.ts +11 -47
  36. package/dist/data-structures/heap/max-heap.js +11 -66
  37. package/dist/data-structures/heap/min-heap.d.ts +12 -47
  38. package/dist/data-structures/heap/min-heap.js +11 -66
  39. package/dist/data-structures/linked-list/doubly-linked-list.d.ts +231 -347
  40. package/dist/data-structures/linked-list/doubly-linked-list.js +368 -494
  41. package/dist/data-structures/linked-list/singly-linked-list.d.ts +261 -310
  42. package/dist/data-structures/linked-list/singly-linked-list.js +447 -466
  43. package/dist/data-structures/linked-list/skip-linked-list.d.ts +0 -107
  44. package/dist/data-structures/linked-list/skip-linked-list.js +0 -100
  45. package/dist/data-structures/priority-queue/max-priority-queue.d.ts +12 -56
  46. package/dist/data-structures/priority-queue/max-priority-queue.js +11 -78
  47. package/dist/data-structures/priority-queue/min-priority-queue.d.ts +11 -57
  48. package/dist/data-structures/priority-queue/min-priority-queue.js +10 -79
  49. package/dist/data-structures/priority-queue/priority-queue.d.ts +2 -61
  50. package/dist/data-structures/priority-queue/priority-queue.js +8 -83
  51. package/dist/data-structures/queue/deque.d.ts +227 -254
  52. package/dist/data-structures/queue/deque.js +309 -348
  53. package/dist/data-structures/queue/queue.d.ts +180 -201
  54. package/dist/data-structures/queue/queue.js +265 -248
  55. package/dist/data-structures/stack/stack.d.ts +124 -102
  56. package/dist/data-structures/stack/stack.js +181 -125
  57. package/dist/data-structures/trie/trie.d.ts +164 -165
  58. package/dist/data-structures/trie/trie.js +189 -172
  59. package/dist/interfaces/binary-tree.d.ts +56 -6
  60. package/dist/interfaces/graph.d.ts +16 -0
  61. package/dist/types/data-structures/base/base.d.ts +1 -1
  62. package/dist/types/data-structures/graph/abstract-graph.d.ts +4 -0
  63. package/dist/types/utils/utils.d.ts +1 -0
  64. package/dist/utils/utils.d.ts +1 -1
  65. package/dist/utils/utils.js +2 -1
  66. package/package.json +2 -2
  67. package/src/data-structures/base/iterable-element-base.ts +238 -115
  68. package/src/data-structures/base/iterable-entry-base.ts +96 -120
  69. package/src/data-structures/base/linear-base.ts +271 -277
  70. package/src/data-structures/binary-tree/avl-tree-counter.ts +198 -216
  71. package/src/data-structures/binary-tree/avl-tree-multi-map.ts +192 -101
  72. package/src/data-structures/binary-tree/avl-tree.ts +239 -206
  73. package/src/data-structures/binary-tree/binary-tree.ts +664 -893
  74. package/src/data-structures/binary-tree/bst.ts +568 -570
  75. package/src/data-structures/binary-tree/red-black-tree.ts +161 -222
  76. package/src/data-structures/binary-tree/tree-counter.ts +199 -218
  77. package/src/data-structures/binary-tree/tree-multi-map.ts +131 -97
  78. package/src/data-structures/graph/abstract-graph.ts +339 -264
  79. package/src/data-structures/graph/directed-graph.ts +146 -236
  80. package/src/data-structures/graph/map-graph.ts +63 -60
  81. package/src/data-structures/graph/undirected-graph.ts +129 -152
  82. package/src/data-structures/hash/hash-map.ts +274 -496
  83. package/src/data-structures/heap/heap.ts +389 -402
  84. package/src/data-structures/heap/max-heap.ts +12 -76
  85. package/src/data-structures/heap/min-heap.ts +13 -76
  86. package/src/data-structures/linked-list/doubly-linked-list.ts +426 -530
  87. package/src/data-structures/linked-list/singly-linked-list.ts +495 -517
  88. package/src/data-structures/linked-list/skip-linked-list.ts +1 -108
  89. package/src/data-structures/priority-queue/max-priority-queue.ts +12 -87
  90. package/src/data-structures/priority-queue/min-priority-queue.ts +11 -88
  91. package/src/data-structures/priority-queue/priority-queue.ts +3 -92
  92. package/src/data-structures/queue/deque.ts +381 -357
  93. package/src/data-structures/queue/queue.ts +310 -264
  94. package/src/data-structures/stack/stack.ts +217 -131
  95. package/src/data-structures/trie/trie.ts +240 -175
  96. package/src/interfaces/binary-tree.ts +240 -6
  97. package/src/interfaces/graph.ts +37 -0
  98. package/src/types/data-structures/base/base.ts +5 -5
  99. package/src/types/data-structures/graph/abstract-graph.ts +5 -0
  100. package/src/types/utils/utils.ts +2 -0
  101. package/src/utils/utils.ts +9 -14
@@ -5,6 +5,7 @@
5
5
  * @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
6
6
  * @license MIT License
7
7
  */
8
+
8
9
  import type {
9
10
  EntryCallback,
10
11
  HashMapLinkedNode,
@@ -16,6 +17,11 @@ import { IterableEntryBase } from '../base';
16
17
  import { isWeakKey, rangeCheck } from '../../utils';
17
18
 
18
19
  /**
20
+ * Hash-based map. Supports object keys and custom hashing; offers O(1) average set/get/has.
21
+ * @remarks Time O(1), Space O(1)
22
+ * @template K
23
+ * @template V
24
+ * @template R
19
25
  * 1. Key-Value Pair Storage: HashMap stores key-value pairs. Each key map to a value.
20
26
  * 2. Fast Lookup: It's used when you need to quickly find, insert, or delete entries based on a key.
21
27
  * 3. Unique Keys: Keys are unique.
@@ -70,11 +76,11 @@ import { isWeakKey, rangeCheck } from '../../utils';
70
76
  */
71
77
  export class HashMap<K = any, V = any, R = [K, V]> extends IterableEntryBase<K, V> {
72
78
  /**
73
- * The constructor function initializes a HashMap object with an optional initial collection and
74
- * options.
75
- * @param entryOrRawElements - The `entryOrRawElements` parameter is an iterable collection of elements of a type
76
- * `T`. It is an optional parameter and its default value is an empty array `[]`.
77
- * @param [options] - The `options` parameter is an optional object that can contain two properties:
79
+ * Create a HashMap and optionally bulk-insert entries.
80
+ * @remarks Time O(N), Space O(N)
81
+ * @param [entryOrRawElements] - Iterable of entries or raw elements to insert.
82
+ * @param [options] - Options: hash function and optional record-to-entry converter.
83
+ * @returns New HashMap instance.
78
84
  */
79
85
  constructor(entryOrRawElements: Iterable<R | [K, V]> = [], options?: HashMapOptions<K, V, R>) {
80
86
  super();
@@ -83,17 +89,15 @@ export class HashMap<K = any, V = any, R = [K, V]> extends IterableEntryBase<K,
83
89
  if (hashFn) this._hashFn = hashFn;
84
90
  if (toEntryFn) this._toEntryFn = toEntryFn;
85
91
  }
86
- if (entryOrRawElements) {
87
- this.setMany(entryOrRawElements);
88
- }
92
+ if (entryOrRawElements) this.setMany(entryOrRawElements);
89
93
  }
90
94
 
91
95
  protected _store: { [key: string]: HashMapStoreItem<K, V> } = {};
92
96
 
93
97
  /**
94
- * The function returns the store object, which is a dictionary of HashMapStoreItem objects.
95
- * @returns The store property is being returned. It is a dictionary-like object with string keys and
96
- * values of type HashMapStoreItem<K, V>.
98
+ * Get the internal store for non-object keys.
99
+ * @remarks Time O(1), Space O(1)
100
+ * @returns Internal record of string→{key,value}.
97
101
  */
98
102
  get store(): { [p: string]: HashMapStoreItem<K, V> } {
99
103
  return this._store;
@@ -102,9 +106,9 @@ export class HashMap<K = any, V = any, R = [K, V]> extends IterableEntryBase<K,
102
106
  protected _objMap: Map<object, V> = new Map();
103
107
 
104
108
  /**
105
- * The function returns the object map.
106
- * @returns The `objMap` property is being returned, which is a `Map` object with keys of type
107
- * `object` and values of type `V`.
109
+ * Get the internal Map used for object/function keys.
110
+ * @remarks Time O(1), Space O(1)
111
+ * @returns Map of object→value.
108
112
  */
109
113
  get objMap(): Map<object, V> {
110
114
  return this._objMap;
@@ -113,8 +117,9 @@ export class HashMap<K = any, V = any, R = [K, V]> extends IterableEntryBase<K,
113
117
  protected _toEntryFn?: (rawElement: R) => [K, V];
114
118
 
115
119
  /**
116
- * The function returns the value of the _toEntryFn property.
117
- * @returns The function being returned is `this._toEntryFn`.
120
+ * Get the raw→entry converter function if present.
121
+ * @remarks Time O(1), Space O(1)
122
+ * @returns Converter function or undefined.
118
123
  */
119
124
  get toEntryFn() {
120
125
  return this._toEntryFn;
@@ -123,8 +128,9 @@ export class HashMap<K = any, V = any, R = [K, V]> extends IterableEntryBase<K,
123
128
  protected _size = 0;
124
129
 
125
130
  /**
126
- * The function returns the size of an object.
127
- * @returns The size of the object, which is a number.
131
+ * Get the number of distinct keys stored.
132
+ * @remarks Time O(1), Space O(1)
133
+ * @returns Current size.
128
134
  */
129
135
  get size(): number {
130
136
  return this._size;
@@ -133,268 +139,210 @@ export class HashMap<K = any, V = any, R = [K, V]> extends IterableEntryBase<K,
133
139
  protected _hashFn: (key: K) => string = (key: K) => String(key);
134
140
 
135
141
  /**
136
- * The hasFn function is a function that takes in an item and returns a boolean
137
- * indicating whether the item is contained within the hash table.
138
- *
139
- * @return The hash function
142
+ * Get the current hash function for non-object keys.
143
+ * @remarks Time O(1), Space O(1)
144
+ * @returns Hash function.
140
145
  */
141
146
  get hashFn() {
142
147
  return this._hashFn;
143
148
  }
144
149
 
145
150
  /**
146
- * Time Complexity: O(1)
147
- * Space Complexity: O(1)
148
- *
149
- * The function checks if a given element is an array with exactly two elements.
150
- * @param {any} rawElement - The `rawElement` parameter is of type `any`, which means it can be any
151
- * data type.
152
- * @returns a boolean value.
153
- */
154
- isEntry(rawElement: any): rawElement is [K, V] {
155
- return Array.isArray(rawElement) && rawElement.length === 2;
156
- }
157
-
158
- /**
159
- * Time Complexity: O(1)
160
- * Space Complexity: O(1)
161
- *
162
- * The function checks if the size of an object is equal to zero and returns a boolean value.
163
- * @returns A boolean value indicating whether the size of the object is 0 or not.
151
+ * Check whether the map is empty.
152
+ * @remarks Time O(1), Space O(1)
153
+ * @returns True if size is 0.
164
154
  */
165
155
  isEmpty(): boolean {
166
156
  return this._size === 0;
167
157
  }
168
158
 
169
159
  /**
170
- * Time Complexity: O(1)
171
- * Space Complexity: O(1)
172
- *
173
- * The clear() function resets the state of an object by clearing its internal store, object map, and
174
- * size.
160
+ * Remove all entries and reset counters.
161
+ * @remarks Time O(N), Space O(1)
162
+ * @returns void
175
163
  */
176
- clear() {
164
+ clear(): void {
177
165
  this._store = {};
178
166
  this._objMap.clear();
179
167
  this._size = 0;
180
168
  }
181
169
 
182
170
  /**
183
- * Time Complexity: O(1)
184
- * Space Complexity: O(1)
185
- *
186
- * The `set` function adds a key-value pair to a map-like data structure, incrementing the size if
187
- * the key is not already present.
188
- * @param {K} key - The key parameter is the key used to identify the value in the data structure. It
189
- * can be of any type, but if it is an object, it will be stored in a Map, otherwise it will be
190
- * stored in a regular JavaScript object.
191
- * @param {V} value - The value parameter represents the value that you want to associate with the
192
- * key in the data structure.
171
+ * Type guard: check if a raw value is a [key, value] entry.
172
+ * @remarks Time O(1), Space O(1)
173
+ * @returns True if the value is a 2-tuple.
174
+ */
175
+ isEntry(rawElement: any): rawElement is [K, V] {
176
+ return Array.isArray(rawElement) && rawElement.length === 2;
177
+ }
178
+
179
+ /**
180
+ * Insert or replace a single entry.
181
+ * @remarks Time O(1), Space O(1)
182
+ * @param key - Key.
183
+ * @param value - Value.
184
+ * @returns True when the operation succeeds.
193
185
  */
194
186
  set(key: K, value: V): boolean {
195
187
  if (this._isObjKey(key)) {
196
- if (!this.objMap.has(key)) {
197
- this._size++;
198
- }
188
+ if (!this.objMap.has(key)) this._size++;
199
189
  this.objMap.set(key, value);
200
190
  } else {
201
191
  const strKey = this._getNoObjKey(key);
202
- if (this.store[strKey] === undefined) {
203
- this._size++;
204
- }
192
+ if (this.store[strKey] === undefined) this._size++;
205
193
  this._store[strKey] = { key, value };
206
194
  }
207
195
  return true;
208
196
  }
209
197
 
210
198
  /**
211
- * Time Complexity: O(k)
212
- * Space Complexity: O(k)
213
- *
214
- * The function `setMany` takes an iterable collection of objects, maps each object to a key-value
215
- * pair using a mapping function, and sets each key-value pair in the current object.
216
- * @param entryOrRawElements - The `entryOrRawElements` parameter is an iterable collection of elements of a type
217
- * `T`.
218
- * @returns The `setMany` function is returning an array of booleans.
199
+ * Insert many entries from an iterable.
200
+ * @remarks Time O(N), Space O(N)
201
+ * @param entryOrRawElements - Iterable of entries or raw elements to insert.
202
+ * @returns Array of per-entry results.
219
203
  */
220
204
  setMany(entryOrRawElements: Iterable<R | [K, V]>): boolean[] {
221
205
  const results: boolean[] = [];
222
206
  for (const rawEle of entryOrRawElements) {
223
207
  let key: K | undefined, value: V | undefined;
224
- if (this.isEntry(rawEle)) {
225
- key = rawEle[0];
226
- value = rawEle[1];
227
- } else if (this._toEntryFn) {
228
- const item = this._toEntryFn(rawEle);
229
- key = item[0];
230
- value = item[1];
231
- }
232
-
208
+ if (this.isEntry(rawEle)) [key, value] = rawEle;
209
+ else if (this._toEntryFn) [key, value] = this._toEntryFn(rawEle);
233
210
  if (key !== undefined && value !== undefined) results.push(this.set(key, value));
234
211
  }
235
212
  return results;
236
213
  }
237
214
 
238
215
  /**
239
- * Time Complexity: O(1)
240
- * Space Complexity: O(1)
241
- *
242
- * The `get` function retrieves a value from a map based on a given key, either from an object map or
243
- * a string map.
244
- * @param {K} key - The `key` parameter is the key used to retrieve a value from the map. It can be
245
- * of any type, but it should be compatible with the key type used when the map was created.
246
- * @returns The method `get(key: K)` returns a value of type `V` if the key exists in the `_objMap`
247
- * or `_store`, otherwise it returns `undefined`.
216
+ * Get the value for a key.
217
+ * @remarks Time O(1), Space O(1)
218
+ * @param key - Key to look up.
219
+ * @returns Value or undefined.
248
220
  */
249
221
  override get(key: K): V | undefined {
250
- if (this._isObjKey(key)) {
251
- return this.objMap.get(key);
252
- } else {
253
- const strKey = this._getNoObjKey(key);
254
- return this._store[strKey]?.value;
255
- }
222
+ if (this._isObjKey(key)) return this.objMap.get(key);
223
+ const strKey = this._getNoObjKey(key);
224
+ return this._store[strKey]?.value;
256
225
  }
257
226
 
258
227
  /**
259
- * Time Complexity: O(1)
260
- * Space Complexity: O(1)
261
- *
262
- * The `has` function checks if a given key exists in the `_objMap` or `_store` based on whether it
263
- * is an object key or not.
264
- * @param {K} key - The parameter "key" is of type K, which means it can be any type.
265
- * @returns The `has` method is returning a boolean value.
228
+ * Check if a key exists.
229
+ * @remarks Time O(1), Space O(1)
230
+ * @param key - Key to test.
231
+ * @returns True if present.
266
232
  */
267
233
  override has(key: K): boolean {
268
- if (this._isObjKey(key)) {
269
- return this.objMap.has(key);
270
- } else {
271
- const strKey = this._getNoObjKey(key);
272
- return strKey in this.store;
273
- }
234
+ if (this._isObjKey(key)) return this.objMap.has(key);
235
+ const strKey = this._getNoObjKey(key);
236
+ return strKey in this.store;
274
237
  }
275
238
 
276
239
  /**
277
- * Time Complexity: O(1)
278
- * Space Complexity: O(1)
279
- *
280
- * The `delete` function removes an element from a map-like data structure based on the provided key.
281
- * @param {K} key - The `key` parameter is the key of the element that you want to delete from the
282
- * data structure.
283
- * @returns The `delete` method returns a boolean value. It returns `true` if the key was
284
- * successfully deleted from the map, and `false` if the key was not found in the map.
240
+ * Delete an entry by key.
241
+ * @remarks Time O(1), Space O(1)
242
+ * @param key - Key to delete.
243
+ * @returns True if the key was found and removed.
285
244
  */
286
245
  delete(key: K): boolean {
287
246
  if (this._isObjKey(key)) {
288
- if (this.objMap.has(key)) {
289
- this._size--;
290
- }
291
-
247
+ if (this.objMap.has(key)) this._size--;
292
248
  return this.objMap.delete(key);
293
- } else {
294
- const strKey = this._getNoObjKey(key);
295
- if (strKey in this.store) {
296
- delete this.store[strKey];
297
- this._size--;
298
- return true;
299
- }
300
- return false;
301
249
  }
250
+ const strKey = this._getNoObjKey(key);
251
+ if (strKey in this.store) {
252
+ delete this.store[strKey];
253
+ this._size--;
254
+ return true;
255
+ }
256
+ return false;
302
257
  }
303
258
 
304
259
  /**
305
- * Time Complexity: O(n)
306
- * Space Complexity: O(n)
307
- *
308
- * The clone function creates a new HashMap with the same key-value pairs as
309
- * this one. The clone function is useful for creating a copy of an existing
310
- * HashMap, and then modifying that copy without affecting the original.
311
- *
312
- * @return A new hashmap with the same values as this one
260
+ * Replace the hash function and rehash the non-object store.
261
+ * @remarks Time O(N), Space O(N)
262
+ * @param fn - New hash function for non-object keys.
263
+ * @returns This map instance.
313
264
  */
314
- clone(): HashMap<K, V, R> {
315
- return new HashMap<K, V, R>(this, { hashFn: this._hashFn, toEntryFn: this._toEntryFn });
265
+ setHashFn(fn: (key: K) => string): this {
266
+ if (this._hashFn === fn) return this;
267
+ this._hashFn = fn;
268
+ this._rehashNoObj();
269
+ return this;
316
270
  }
317
271
 
318
272
  /**
319
- * Time Complexity: O(n)
320
- * Space Complexity: O(n)
321
- *
322
- * The `map` function in TypeScript creates a new HashMap by applying a callback function to each
323
- * key-value pair in the original HashMap.
324
- * @param callbackfn - The callback function that will be called for each key-value pair in the
325
- * HashMap. It takes four parameters:
326
- * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that specifies the value
327
- * to be used as `this` when executing the `callbackfn` function. If `thisArg` is provided, it will
328
- * be passed as the `this` value to the `callbackfn` function. If `thisArg
329
- * @returns The `map` method is returning a new `HashMap` object with the transformed values based on
330
- * the provided callback function.
273
+ * Deep clone this map, preserving hashing behavior.
274
+ * @remarks Time O(N), Space O(N)
275
+ * @returns A new map with the same content.
331
276
  */
332
- map<VM>(callbackfn: EntryCallback<K, V, VM>, thisArg?: any): HashMap<K, VM> {
333
- const resultMap = new HashMap<K, VM>();
334
- let index = 0;
335
- for (const [key, value] of this) {
336
- resultMap.set(key, callbackfn.call(thisArg, key, value, index++, this));
337
- }
338
- return resultMap;
339
- }
340
-
341
- /**
342
- * Time Complexity: O(n)
343
- * Space Complexity: O(n)
344
- *
345
- * The `filter` function creates a new HashMap containing key-value pairs from the original HashMap
346
- * that satisfy a given predicate function.
347
- * @param predicate - The predicate parameter is a function that takes four arguments: value, key,
348
- * index, and map. It is used to determine whether an element should be included in the filtered map
349
- * or not. The function should return a boolean value - true if the element should be included, and
350
- * false otherwise.
351
- * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that specifies the value
352
- * to be used as `this` when executing the `predicate` function. If `thisArg` is provided, it will be
353
- * passed as the `this` value to the `predicate` function. If `thisArg` is
354
- * @returns The `filter` method is returning a new `HashMap` object that contains the key-value pairs
355
- * from the original `HashMap` that pass the provided `predicate` function.
356
- */
357
- filter(predicate: EntryCallback<K, V, boolean>, thisArg?: any): HashMap<K, V> {
358
- const filteredMap = new HashMap<K, V>();
277
+ clone(): this {
278
+ const opts = { hashFn: this._hashFn, toEntryFn: this._toEntryFn };
279
+ return this._createLike<[K, V], [K, V], [K, V]>(this, opts);
280
+ }
281
+
282
+ /**
283
+ * Map values to a new map with the same keys.
284
+ * @remarks Time O(N), Space O(N)
285
+ * @template VM
286
+ * @param callbackfn - Mapping function (key, value, index, map) → newValue.
287
+ * @param [thisArg] - Value for `this` inside the callback.
288
+ * @returns A new map with transformed values.
289
+ */
290
+ map<VM>(callbackfn: EntryCallback<K, V, VM>, thisArg?: any): any {
291
+ const out = this._createLike<K, VM, [K, VM]>();
359
292
  let index = 0;
360
- for (const [key, value] of this) {
361
- if (predicate.call(thisArg, key, value, index++, this)) {
362
- filteredMap.set(key, value);
363
- }
364
- }
365
- return filteredMap;
293
+ for (const [key, value] of this) out.set(key, callbackfn.call(thisArg, key, value, index++, this));
294
+ return out;
366
295
  }
367
296
 
368
297
  /**
369
- * The function returns an iterator that yields key-value pairs from both an object store and an
370
- * object map.
298
+ * Filter entries into a new map.
299
+ * @remarks Time O(N), Space O(N)
300
+ * @param predicate - Predicate (key, value, index, map) → boolean.
301
+ * @param [thisArg] - Value for `this` inside the predicate.
302
+ * @returns A new map containing entries that satisfied the predicate.
371
303
  */
372
- protected *_getIterator(): IterableIterator<[K, V]> {
373
- for (const node of Object.values(this.store)) {
374
- yield [node.key, node.value] as [K, V];
375
- }
376
- for (const node of this.objMap) {
377
- yield node as [K, V];
378
- }
304
+
305
+ filter(predicate: EntryCallback<K, V, boolean>, thisArg?: any): any {
306
+ const out = this._createLike<K, V, [K, V]>();
307
+ let index = 0;
308
+ for (const [key, value] of this) if (predicate.call(thisArg, key, value, index++, this)) out.set(key, value);
309
+ return out;
379
310
  }
380
311
 
381
312
  /**
382
- * The function checks if a given key is an object or a function.
383
- * @param {any} key - The parameter "key" can be of any type.
384
- * @returns a boolean value.
313
+ * (Protected) Create a like-kind instance and seed it from an iterable.
314
+ * @remarks Time O(N), Space O(N)
315
+ * @template TK
316
+ * @template TV
317
+ * @template TR
318
+ * @param [entries] - Iterable used to seed the new map.
319
+ * @param [options] - Options forwarded to the constructor.
320
+ * @returns A like-kind map instance.
385
321
  */
322
+ protected _createLike<TK = K, TV = V, TR = [TK, TV]>(entries: Iterable<[TK, TV] | TR> = [], options?: any): any {
323
+ const Ctor = this.constructor as new (e?: Iterable<[TK, TV] | TR>, o?: any) => any;
324
+ return new Ctor(entries, options);
325
+ }
326
+
327
+ protected _rehashNoObj(): void {
328
+ const fresh: Record<string, HashMapStoreItem<K, V>> = {};
329
+ for (const { key, value } of Object.values(this._store)) {
330
+ const sk = this._getNoObjKey(key);
331
+ fresh[sk] = { key, value };
332
+ }
333
+ this._store = fresh;
334
+ }
335
+
336
+ protected *_getIterator(): IterableIterator<[K, V]> {
337
+ for (const node of Object.values(this.store)) yield [node.key, node.value] as [K, V];
338
+ for (const node of this.objMap) yield node as [K, V];
339
+ }
340
+
386
341
  protected _isObjKey(key: any): key is object | ((...args: any[]) => any) {
387
342
  const keyType = typeof key;
388
343
  return (keyType === 'object' || keyType === 'function') && key !== null;
389
344
  }
390
345
 
391
- /**
392
- * The function `_getNoObjKey` takes a key and returns a string representation of the key, handling
393
- * different types of keys.
394
- * @param {K} key - The `key` parameter is of type `K`, which represents the type of the key being
395
- * passed to the `_getNoObjKey` function.
396
- * @returns a string value.
397
- */
398
346
  protected _getNoObjKey(key: K): string {
399
347
  const keyType = typeof key;
400
348
 
@@ -403,7 +351,6 @@ export class HashMap<K = any, V = any, R = [K, V]> extends IterableEntryBase<K,
403
351
  strKey = this._hashFn(key);
404
352
  } else {
405
353
  if (keyType === 'number') {
406
- // TODO numeric key should has its own hash
407
354
  strKey = <string>key;
408
355
  } else {
409
356
  strKey = <string>key;
@@ -414,21 +361,22 @@ export class HashMap<K = any, V = any, R = [K, V]> extends IterableEntryBase<K,
414
361
  }
415
362
 
416
363
  /**
417
- * 1. Maintaining the Order of Element Insertion: Unlike HashMap, LinkedHashMap maintains the order in which entries are inserted. Therefore, when you traverse it, entries will be returned in the order they were inserted into the map.
418
- * 2. Based on Hash Table and Linked List: It combines the structures of a hash table and a linked list, using the hash table to ensure fast access, while maintaining the order of entries through the linked list.
419
- * 3. Time Complexity: Similar to HashMap, LinkedHashMap offers constant-time performance for get and put operations in most cases.
364
+ * Hash-based map that preserves insertion order via a doubly-linked list.
365
+ * @remarks Time O(1), Space O(1)
366
+ * @template K
367
+ * @template V
368
+ * @template R
369
+ * @example examples will be generated by unit test
420
370
  */
421
371
  export class LinkedHashMap<K = any, V = any, R = [K, V]> extends IterableEntryBase<K, V> {
422
372
  protected readonly _sentinel: HashMapLinkedNode<K, V | undefined>;
423
373
 
424
374
  /**
425
- * The constructor initializes a LinkedHashMap object with an optional raw collection and options.
426
- * @param entryOrRawElements - The `entryOrRawElements` parameter is an iterable collection of elements. It is
427
- * used to initialize the HashMapLinked instance with key-value pairs. Each element in the
428
- * `entryOrRawElements` is converted to a key-value pair using the `toEntryFn` function (if provided) and
429
- * then added to the HashMap
430
- * @param [options] - The `options` parameter is an optional object that can contain the following
431
- * properties:
375
+ * Create a LinkedHashMap and optionally bulk-insert entries.
376
+ * @remarks Time O(N), Space O(N)
377
+ * @param [entryOrRawElements] - Iterable of entries or raw elements to insert.
378
+ * @param [options] - Options: hash functions and optional record-to-entry converter.
379
+ * @returns New LinkedHashMap instance.
432
380
  */
433
381
  constructor(entryOrRawElements: Iterable<R | [K, V]> = [], options?: LinkedHashMapOptions<K, V, R>) {
434
382
  super();
@@ -439,23 +387,13 @@ export class LinkedHashMap<K = any, V = any, R = [K, V]> extends IterableEntryBa
439
387
  const { hashFn, objHashFn, toEntryFn } = options;
440
388
  if (hashFn) this._hashFn = hashFn;
441
389
  if (objHashFn) this._objHashFn = objHashFn;
442
-
443
- if (toEntryFn) {
444
- this._toEntryFn = toEntryFn;
445
- }
390
+ if (toEntryFn) this._toEntryFn = toEntryFn;
446
391
  }
447
392
 
448
- if (entryOrRawElements) {
449
- this.setMany(entryOrRawElements);
450
- }
393
+ if (entryOrRawElements) this.setMany(entryOrRawElements);
451
394
  }
452
395
 
453
396
  protected _hashFn: (key: K) => string = (key: K) => String(key);
454
-
455
- /**
456
- * The function returns the hash function used for generating a hash value for a given key.
457
- * @returns The hash function that takes a key of type K and returns a string.
458
- */
459
397
  get hashFn(): (key: K) => string {
460
398
  return this._hashFn;
461
399
  }
@@ -463,8 +401,9 @@ export class LinkedHashMap<K = any, V = any, R = [K, V]> extends IterableEntryBa
463
401
  protected _objHashFn: (key: K) => object = (key: K) => <object>key;
464
402
 
465
403
  /**
466
- * The function returns the object hash function.
467
- * @returns The function `objHashFn` is being returned.
404
+ * Get the hash function for object/weak keys.
405
+ * @remarks Time O(1), Space O(1)
406
+ * @returns Object-hash function.
468
407
  */
469
408
  get objHashFn(): (key: K) => object {
470
409
  return this._objHashFn;
@@ -473,21 +412,15 @@ export class LinkedHashMap<K = any, V = any, R = [K, V]> extends IterableEntryBa
473
412
  protected _noObjMap: Record<string, HashMapLinkedNode<K, V | undefined>> = {};
474
413
 
475
414
  /**
476
- * The function returns a record of HashMapLinkedNode objects with string keys.
477
- * @returns The method is returning a Record object, which is a TypeScript type that represents an
478
- * object with string keys and values that are HashMapLinkedNode objects with keys of type K and
479
- * values of type V or undefined.
415
+ * Get the internal record for non-object keys.
416
+ * @remarks Time O(1), Space O(1)
417
+ * @returns Record of hash→node.
480
418
  */
481
419
  get noObjMap(): Record<string, HashMapLinkedNode<K, V | undefined>> {
482
420
  return this._noObjMap;
483
421
  }
484
422
 
485
423
  protected _objMap = new WeakMap<object, HashMapLinkedNode<K, V | undefined>>();
486
-
487
- /**
488
- * The function returns the WeakMap object used to map objects to HashMapLinkedNode instances.
489
- * @returns The `objMap` property is being returned.
490
- */
491
424
  get objMap(): WeakMap<object, HashMapLinkedNode<K, V | undefined>> {
492
425
  return this._objMap;
493
426
  }
@@ -495,9 +428,9 @@ export class LinkedHashMap<K = any, V = any, R = [K, V]> extends IterableEntryBa
495
428
  protected _head: HashMapLinkedNode<K, V | undefined>;
496
429
 
497
430
  /**
498
- * The function returns the head node of a HashMapLinkedNode.
499
- * @returns The method `getHead()` is returning a `HashMapLinkedNode` object with key type `K` and
500
- * a value type `V | undefined`.
431
+ * Get the head node (first entry) sentinel link.
432
+ * @remarks Time O(1), Space O(1)
433
+ * @returns Head node or sentinel.
501
434
  */
502
435
  get head(): HashMapLinkedNode<K, V | undefined> {
503
436
  return this._head;
@@ -506,8 +439,9 @@ export class LinkedHashMap<K = any, V = any, R = [K, V]> extends IterableEntryBa
506
439
  protected _tail: HashMapLinkedNode<K, V | undefined>;
507
440
 
508
441
  /**
509
- * The function returns the tail node of a HashMapLinkedNode.
510
- * @returns The `_tail` property of type `HashMapLinkedNode<K, V | undefined>` is being returned.
442
+ * Get the tail node (last entry) sentinel link.
443
+ * @remarks Time O(1), Space O(1)
444
+ * @returns Tail node or sentinel.
511
445
  */
512
446
  get tail(): HashMapLinkedNode<K, V | undefined> {
513
447
  return this._tail;
@@ -515,40 +449,25 @@ export class LinkedHashMap<K = any, V = any, R = [K, V]> extends IterableEntryBa
515
449
 
516
450
  protected _toEntryFn?: (rawElement: R) => [K, V] = (rawElement: R) => {
517
451
  if (this.isEntry(rawElement)) {
518
- // TODO, For performance optimization, it may be necessary to only inspect the first element traversed.
519
452
  return rawElement;
520
- } else {
521
- throw new Error(
522
- "If the provided entryOrRawElements does not adhere to the [key, value] type format, the toEntryFn in the constructor's options parameter needs to specified."
523
- );
524
453
  }
454
+ throw new Error(
455
+ 'If `entryOrRawElements` does not adhere to [key,value], provide `options.toEntryFn` to transform raw records.'
456
+ );
525
457
  };
526
-
527
- /**
528
- * The function returns the value of the _toEntryFn property.
529
- * @returns The function being returned is `this._toEntryFn`.
530
- */
531
458
  get toEntryFn() {
532
459
  return this._toEntryFn;
533
460
  }
534
461
 
535
462
  protected _size = 0;
536
-
537
- /**
538
- * The function returns the size of an object.
539
- * @returns The size of the object.
540
- */
541
463
  get size() {
542
464
  return this._size;
543
465
  }
544
466
 
545
467
  /**
546
- * Time Complexity: O(1)
547
- * Space Complexity: O(1)
548
- *
549
- * The function returns the key-value pair at the front of a data structure.
550
- * @returns The front element of the data structure, represented as a tuple with a key (K) and a
551
- * value (V).
468
+ * Get the first [key, value] pair.
469
+ * @remarks Time O(1), Space O(1)
470
+ * @returns First entry or undefined when empty.
552
471
  */
553
472
  get first() {
554
473
  if (this._size === 0) return;
@@ -556,12 +475,9 @@ export class LinkedHashMap<K = any, V = any, R = [K, V]> extends IterableEntryBa
556
475
  }
557
476
 
558
477
  /**
559
- * Time Complexity: O(1)
560
- * Space Complexity: O(1)
561
- *
562
- * The function returns the key-value pair at the end of a data structure.
563
- * @returns The method is returning an array containing the key-value pair of the tail element in the
564
- * data structure.
478
+ * Get the last [key, value] pair.
479
+ * @remarks Time O(1), Space O(1)
480
+ * @returns Last entry or undefined when empty.
565
481
  */
566
482
  get last() {
567
483
  if (this._size === 0) return;
@@ -569,7 +485,9 @@ export class LinkedHashMap<K = any, V = any, R = [K, V]> extends IterableEntryBa
569
485
  }
570
486
 
571
487
  /**
572
- * The `begin()` function in TypeScript iterates over a linked list and yields key-value pairs.
488
+ * Iterate from head tail.
489
+ * @remarks Time O(N), Space O(1)
490
+ * @returns Iterator of [key, value].
573
491
  */
574
492
  *begin() {
575
493
  let node = this.head;
@@ -580,8 +498,9 @@ export class LinkedHashMap<K = any, V = any, R = [K, V]> extends IterableEntryBa
580
498
  }
581
499
 
582
500
  /**
583
- * The function `reverseBegin()` iterates over a linked list in reverse order, yielding each node's
584
- * key and value.
501
+ * Iterate from tail head.
502
+ * @remarks Time O(N), Space O(1)
503
+ * @returns Iterator of [key, value].
585
504
  */
586
505
  *reverseBegin() {
587
506
  let node = this.tail;
@@ -592,53 +511,42 @@ export class LinkedHashMap<K = any, V = any, R = [K, V]> extends IterableEntryBa
592
511
  }
593
512
 
594
513
  /**
595
- * Time Complexity: O(1)
596
- * Space Complexity: O(1)
597
- *
598
- * The `set` function adds a new key-value pair to a data structure, either using an object key or a
599
- * string key.
600
- * @param {K} key - The `key` parameter is the key to be set in the data structure. It can be of any
601
- * type, but typically it is a string or symbol.
602
- * @param {V} [value] - The `value` parameter is an optional parameter of type `V`. It represents the
603
- * value associated with the key being set in the data structure.
604
- * @returns the size of the data structure after the key-value pair has been set.
514
+ * Insert or replace a single entry; preserves insertion order.
515
+ * @remarks Time O(1), Space O(1)
516
+ * @param key - Key.
517
+ * @param [value] - Value.
518
+ * @returns True when the operation succeeds.
605
519
  */
606
520
  set(key: K, value?: V): boolean {
607
- let node;
608
- const isNewKey = !this.has(key); // Check if the key is new
521
+ let node: HashMapLinkedNode<K, V | undefined> | undefined;
522
+ const isNewKey = !this.has(key);
609
523
 
610
524
  if (isWeakKey(key)) {
611
525
  const hash = this._objHashFn(key);
612
526
  node = this.objMap.get(hash);
613
-
614
527
  if (!node && isNewKey) {
615
- // Create a new node
616
528
  node = { key: <K>hash, value, prev: this.tail, next: this._sentinel };
617
529
  this.objMap.set(hash, node);
618
530
  } else if (node) {
619
- // Update the value of an existing node
620
531
  node.value = value;
621
532
  }
622
533
  } else {
623
534
  const hash = this._hashFn(key);
624
535
  node = this.noObjMap[hash];
625
-
626
536
  if (!node && isNewKey) {
627
537
  this.noObjMap[hash] = node = { key, value, prev: this.tail, next: this._sentinel };
628
538
  } else if (node) {
629
- // Update the value of an existing node
630
539
  node.value = value;
631
540
  }
632
541
  }
633
542
 
634
543
  if (node && isNewKey) {
635
- // Update the head and tail of the linked list
636
544
  if (this._size === 0) {
637
545
  this._head = node;
638
546
  this._sentinel.next = node;
639
547
  } else {
640
548
  this.tail.next = node;
641
- node.prev = this.tail; // Make sure that the prev of the new node points to the current tail node
549
+ node.prev = this.tail;
642
550
  }
643
551
  this._tail = node;
644
552
  this._sentinel.prev = node;
@@ -648,274 +556,154 @@ export class LinkedHashMap<K = any, V = any, R = [K, V]> extends IterableEntryBa
648
556
  return true;
649
557
  }
650
558
 
651
- /**
652
- * Time Complexity: O(k)
653
- * Space Complexity: O(k)
654
- *
655
- * The function `setMany` takes an iterable collection, converts each element into a key-value pair
656
- * using a provided function, and sets each key-value pair in the current object, returning an array
657
- * of booleans indicating the success of each set operation.
658
- * @param entryOrRawElements - The entryOrRawElements parameter is an iterable collection of elements of type
659
- * R.
660
- * @returns The `setMany` function returns an array of booleans.
661
- */
662
559
  setMany(entryOrRawElements: Iterable<R | [K, V]>): boolean[] {
663
560
  const results: boolean[] = [];
664
561
  for (const rawEle of entryOrRawElements) {
665
562
  let key: K | undefined, value: V | undefined;
666
- if (this.isEntry(rawEle)) {
667
- key = rawEle[0];
668
- value = rawEle[1];
669
- } else if (this._toEntryFn) {
670
- const item = this._toEntryFn(rawEle);
671
- key = item[0];
672
- value = item[1];
673
- }
674
-
563
+ if (this.isEntry(rawEle)) [key, value] = rawEle;
564
+ else if (this._toEntryFn) [key, value] = this._toEntryFn(rawEle);
675
565
  if (key !== undefined && value !== undefined) results.push(this.set(key, value));
676
566
  }
677
567
  return results;
678
568
  }
679
569
 
680
- /**
681
- * Time Complexity: O(1)
682
- * Space Complexity: O(1)
683
- *
684
- * The function checks if a given key exists in a map, using different logic depending on whether the
685
- * key is a weak key or not.
686
- * @param {K} key - The `key` parameter is the key that is being checked for existence in the map.
687
- * @returns The method `has` is returning a boolean value.
688
- */
689
570
  override has(key: K): boolean {
690
571
  if (isWeakKey(key)) {
691
572
  const hash = this._objHashFn(key);
692
573
  return this.objMap.has(hash);
693
- } else {
694
- const hash = this._hashFn(key);
695
- return hash in this.noObjMap;
696
574
  }
575
+ const hash = this._hashFn(key);
576
+ return hash in this.noObjMap;
697
577
  }
698
578
 
699
- /**
700
- * Time Complexity: O(1)
701
- * Space Complexity: O(1)
702
- *
703
- * The function `get` retrieves the value associated with a given key from a map, either by using the
704
- * key directly or by using an index stored in the key object.
705
- * @param {K} key - The `key` parameter is the key used to retrieve a value from the map. It can be
706
- * of any type, but typically it is a string or symbol.
707
- * @returns The value associated with the given key is being returned. If the key is an object key,
708
- * the value is retrieved from the `_nodes` array using the index stored in the `OBJ_KEY_INDEX`
709
- * property of the key. If the key is a string key, the value is retrieved from the `_noObjMap` object
710
- * using the key itself. If the key is not found, `undefined` is
711
- */
712
579
  override get(key: K): V | undefined {
713
580
  if (isWeakKey(key)) {
714
581
  const hash = this._objHashFn(key);
715
582
  const node = this.objMap.get(hash);
716
583
  return node ? node.value : undefined;
717
- } else {
718
- const hash = this._hashFn(key);
719
- const node = this.noObjMap[hash];
720
- return node ? node.value : undefined;
721
584
  }
585
+ const hash = this._hashFn(key);
586
+ const node = this.noObjMap[hash];
587
+ return node ? node.value : undefined;
722
588
  }
723
589
 
724
590
  /**
725
- * Time Complexity: O(n)
726
- * Space Complexity: O(1)
727
- *
728
- * The function `at` retrieves the key-value pair at a specified index in a linked list.
729
- * @param {number} index - The index parameter is a number that represents the position of the
730
- * element we want to retrieve from the data structure.
731
- * @returns The method `at(index: number)` is returning an array containing the key-value pair at
732
- * the specified index in the data structure. The key-value pair is represented as a tuple `[K, V]`,
733
- * where `K` is the key and `V` is the value.
591
+ * Get the value at a given index in insertion order.
592
+ * @remarks Time O(N), Space O(1)
593
+ * @param index - Zero-based index.
594
+ * @returns Value at the index.
734
595
  */
735
596
  at(index: number): V | undefined {
736
597
  rangeCheck(index, 0, this._size - 1);
737
598
  let node = this.head;
738
- while (index--) {
739
- node = node.next;
740
- }
599
+ while (index--) node = node.next;
741
600
  return node.value;
742
601
  }
743
602
 
744
- /**
745
- * Time Complexity: O(1)
746
- * Space Complexity: O(1)
747
- *
748
- * The `delete` function removes a key-value pair from a map-like data structure.
749
- * @param {K} key - The `key` parameter is the key that you want to delete from the data structure.
750
- * It can be of any type, but typically it is a string or an object.
751
- * @returns a boolean value. It returns `true` if the deletion was successful, and `false` if the key
752
- * was not found.
753
- */
754
603
  delete(key: K): boolean {
755
- let node;
756
-
604
+ let node: HashMapLinkedNode<K, V | undefined> | undefined;
757
605
  if (isWeakKey(key)) {
758
606
  const hash = this._objHashFn(key);
759
- // Get nodes from WeakMap
760
607
  node = this.objMap.get(hash);
761
-
762
- if (!node) {
763
- return false; // If the node does not exist, return false
764
- }
765
-
766
- // Remove nodes from WeakMap
608
+ if (!node) return false;
767
609
  this.objMap.delete(hash);
768
610
  } else {
769
611
  const hash = this._hashFn(key);
770
- // Get nodes from noObjMap
771
612
  node = this.noObjMap[hash];
772
-
773
- if (!node) {
774
- return false; // If the node does not exist, return false
775
- }
776
-
777
- // Remove nodes from orgMap
613
+ if (!node) return false;
778
614
  delete this.noObjMap[hash];
779
615
  }
616
+ return this._deleteNode(node);
617
+ }
780
618
 
781
- // Remove node from doubly linked list
782
- this._deleteNode(node);
783
- return true;
619
+ /**
620
+ * Delete the first entry that matches a predicate.
621
+ * @remarks Time O(N), Space O(1)
622
+ * @param predicate - Function (key, value, index, map) → boolean to decide deletion.
623
+ * @returns True if an entry was removed.
624
+ */
625
+ deleteWhere(predicate: (key: K, value: V | undefined, index: number, map: this) => boolean): boolean {
626
+ let node = this._head;
627
+ let i = 0;
628
+ while (node !== this._sentinel) {
629
+ const cur = node;
630
+ node = node.next;
631
+ if (predicate(cur.key as K, cur.value as V | undefined, i++, this)) {
632
+ if (isWeakKey(cur.key as unknown as object)) {
633
+ this._objMap.delete(cur.key as unknown as object);
634
+ } else {
635
+ const hash = this._hashFn(cur.key as K);
636
+ delete this._noObjMap[hash];
637
+ }
638
+ return this._deleteNode(cur);
639
+ }
640
+ }
641
+ return false;
784
642
  }
785
643
 
786
644
  /**
787
- * Time Complexity: O(n)
788
- * Space Complexity: O(1)
789
- *
790
- * The `deleteAt` function deletes a node at a specified index in a linked list.
791
- * @param {number} index - The index parameter represents the position at which the node should be
792
- * deleted in the linked list.
793
- * @returns The size of the list after deleting the element at the specified index.
645
+ * Delete the entry at a given index.
646
+ * @remarks Time O(N), Space O(1)
647
+ * @param index - Zero-based index.
648
+ * @returns True if removed.
794
649
  */
795
650
  deleteAt(index: number): boolean {
796
651
  rangeCheck(index, 0, this._size - 1);
797
652
  let node = this.head;
798
- while (index--) {
799
- node = node.next;
800
- }
653
+ while (index--) node = node.next;
801
654
  return this._deleteNode(node);
802
655
  }
803
656
 
804
- /**
805
- * Time Complexity: O(1)
806
- * Space Complexity: O(1)
807
- *
808
- * The function checks if a data structure is empty by comparing its size to zero.
809
- * @returns The method is returning a boolean value indicating whether the size of the object is 0 or
810
- * not.
811
- */
812
657
  isEmpty(): boolean {
813
658
  return this._size === 0;
814
659
  }
815
660
 
816
- /**
817
- * The function checks if a given element is an array with exactly two elements.
818
- * @param {any} rawElement - The `rawElement` parameter is of type `any`, which means it can be any
819
- * data type.
820
- * @returns a boolean value.
821
- */
822
661
  isEntry(rawElement: any): rawElement is [K, V] {
823
662
  return Array.isArray(rawElement) && rawElement.length === 2;
824
663
  }
825
664
 
826
- /**
827
- * Time Complexity: O(1)
828
- * Space Complexity: O(1)
829
- *
830
- * The `clear` function clears all the entries in a data structure and resets its properties.
831
- */
832
665
  clear(): void {
833
666
  this._noObjMap = {};
834
667
  this._size = 0;
835
668
  this._head = this._tail = this._sentinel.prev = this._sentinel.next = this._sentinel;
836
669
  }
837
670
 
838
- /**
839
- * Time Complexity: O(n)
840
- * Space Complexity: O(n)
841
- *
842
- * The `clone` function creates a new instance of a `LinkedHashMap` with the same key-value pairs as
843
- * the original.
844
- * @returns The `clone()` method is returning a new instance of `LinkedHashMap<K, V>` that is a clone
845
- * of the original `LinkedHashMap` object.
846
- */
847
- clone(): LinkedHashMap<K, V> {
848
- const cloned = new LinkedHashMap<K, V>([], { hashFn: this._hashFn, objHashFn: this._objHashFn });
849
- for (const entry of this) {
850
- const [key, value] = entry;
851
- cloned.set(key, value);
852
- }
853
- return cloned;
854
- }
855
-
856
- /**
857
- * Time Complexity: O(n)
858
- * Space Complexity: O(n)
859
- *
860
- * The `filter` function creates a new `LinkedHashMap` containing key-value pairs from the original
861
- * map that satisfy a given predicate function.
862
- * @param predicate - The `predicate` parameter is a callback function that takes four arguments:
863
- * `value`, `key`, `index`, and `this`. It should return a boolean value indicating whether the
864
- * current element should be included in the filtered map or not.
865
- * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that allows you to
866
- * specify the value of `this` within the `predicate` function. It is used when you want to bind a
867
- * specific object as the context for the `predicate` function. If `thisArg` is not provided, `this
868
- * @returns a new `LinkedHashMap` object that contains the key-value pairs from the original
869
- * `LinkedHashMap` object that satisfy the given predicate function.
870
- */
871
- filter(predicate: EntryCallback<K, V, boolean>, thisArg?: any): LinkedHashMap<K, V> {
872
- const filteredMap = new LinkedHashMap<K, V>();
671
+ clone(): any {
672
+ const opts = { hashFn: this._hashFn, objHashFn: this._objHashFn };
673
+ return this._createLike<[K, V], [K, V], [K, V]>(this, opts);
674
+ }
675
+
676
+ filter(predicate: EntryCallback<K, V, boolean>, thisArg?: any): any {
677
+ const out = this._createLike<K, V, [K, V]>();
873
678
  let index = 0;
874
679
  for (const [key, value] of this) {
875
- if (predicate.call(thisArg, key, value, index, this)) {
876
- filteredMap.set(key, value);
877
- }
680
+ if (predicate.call(thisArg, key, value, index, this)) out.set(key, value);
878
681
  index++;
879
682
  }
880
- return filteredMap;
881
- }
882
-
883
- /**
884
- * Time Complexity: O(n)
885
- * Space Complexity: O(n)
886
- *
887
- * The `map` function in TypeScript creates a new `LinkedHashMap` by applying a callback function to
888
- * each key-value pair in the original map.
889
- * @param callback - The callback parameter is a function that will be called for each key-value pair
890
- * in the map. It takes four arguments: the value of the current key-value pair, the key of the
891
- * current key-value pair, the index of the current key-value pair, and the map itself. The callback
892
- * function should
893
- * @param {any} [thisArg] - The `thisArg` parameter is an optional argument that allows you to
894
- * specify the value of `this` within the callback function. If provided, the callback function will
895
- * be called with `thisArg` as its `this` value. If not provided, `this` will refer to the current
896
- * map
897
- * @returns a new `LinkedHashMap` object with the values mapped according to the provided callback
898
- * function.
899
- */
900
- map<MK, MV>(callback: EntryCallback<K, V, [MK, MV]>, thisArg?: any): LinkedHashMap<MK, MV> {
901
- const mappedMap = new LinkedHashMap<MK, MV>();
683
+ return out;
684
+ }
685
+
686
+ /**
687
+ * Map each entry to a new [key, value] pair and preserve order.
688
+ * @remarks Time O(N), Space O(N)
689
+ * @template MK
690
+ * @template MV
691
+ * @param callback - Mapping function (key, value, index, map) [newKey, newValue].
692
+ * @param [thisArg] - Value for `this` inside the callback.
693
+ * @returns A new map of the same class with transformed entries.
694
+ */
695
+ map<MK, MV>(callback: EntryCallback<K, V, [MK, MV]>, thisArg?: any): any {
696
+ const out = this._createLike<MK, MV, [MK, MV]>();
902
697
  let index = 0;
903
698
  for (const [key, value] of this) {
904
699
  const [newKey, newValue] = callback.call(thisArg, key, value, index, this);
905
- mappedMap.set(newKey, newValue);
700
+ out.set(newKey, newValue);
906
701
  index++;
907
702
  }
908
- return mappedMap;
703
+ return out;
909
704
  }
910
705
 
911
- /**
912
- * Time Complexity: O(n)
913
- * Space Complexity: O(1)
914
- * where n is the number of entries in the LinkedHashMap.
915
- *
916
- * The above function is an iterator that yields key-value pairs from a linked list.
917
- */
918
- protected *_getIterator() {
706
+ protected *_getIterator(): IterableIterator<[K, V]> {
919
707
  let node = this.head;
920
708
  while (node !== this._sentinel) {
921
709
  yield [node.key, node.value] as [K, V];
@@ -923,30 +711,20 @@ export class LinkedHashMap<K = any, V = any, R = [K, V]> extends IterableEntryBa
923
711
  }
924
712
  }
925
713
 
926
- /**
927
- * Time Complexity: O(1)
928
- * Space Complexity: O(1)
929
- *
930
- * The `_deleteNode` function removes a node from a doubly linked list and updates the head and tail
931
- * pointers if necessary.
932
- * @param node - The `node` parameter is an instance of the `HashMapLinkedNode` class, which
933
- * represents a node in a linked list. It contains a key-value pair and references to the previous
934
- * and next nodes in the list.
935
- */
936
714
  protected _deleteNode(node: HashMapLinkedNode<K, V | undefined>): boolean {
937
715
  const { prev, next } = node;
938
716
  prev.next = next;
939
717
  next.prev = prev;
940
718
 
941
- if (node === this.head) {
942
- this._head = next;
943
- }
944
-
945
- if (node === this.tail) {
946
- this._tail = prev;
947
- }
719
+ if (node === this.head) this._head = next;
720
+ if (node === this.tail) this._tail = prev;
948
721
 
949
722
  this._size -= 1;
950
723
  return true;
951
724
  }
725
+
726
+ protected _createLike<TK = K, TV = V, TR = [TK, TV]>(entries: Iterable<[TK, TV] | TR> = [], options?: any): any {
727
+ const Ctor = this.constructor as new (e?: Iterable<[TK, TV] | TR>, o?: any) => any;
728
+ return new Ctor(entries, options);
729
+ }
952
730
  }