graph-typed 2.4.4 → 2.5.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/dist/cjs/index.cjs +1324 -220
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs-legacy/index.cjs +1325 -219
- package/dist/cjs-legacy/index.cjs.map +1 -1
- package/dist/esm/index.mjs +1324 -221
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm-legacy/index.mjs +1325 -220
- package/dist/esm-legacy/index.mjs.map +1 -1
- package/dist/types/common/error.d.ts +23 -0
- package/dist/types/common/index.d.ts +1 -0
- package/dist/types/data-structures/base/iterable-element-base.d.ts +1 -1
- package/dist/types/data-structures/binary-tree/avl-tree.d.ts +128 -51
- package/dist/types/data-structures/binary-tree/binary-indexed-tree.d.ts +210 -164
- package/dist/types/data-structures/binary-tree/binary-tree.d.ts +439 -78
- package/dist/types/data-structures/binary-tree/bst.d.ts +311 -28
- package/dist/types/data-structures/binary-tree/red-black-tree.d.ts +217 -31
- package/dist/types/data-structures/binary-tree/segment-tree.d.ts +218 -152
- package/dist/types/data-structures/binary-tree/tree-map.d.ts +1281 -5
- package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +1087 -201
- package/dist/types/data-structures/binary-tree/tree-multi-set.d.ts +858 -65
- package/dist/types/data-structures/binary-tree/tree-set.d.ts +1133 -5
- package/dist/types/data-structures/graph/abstract-graph.d.ts +44 -0
- package/dist/types/data-structures/graph/directed-graph.d.ts +220 -47
- package/dist/types/data-structures/graph/map-graph.d.ts +59 -1
- package/dist/types/data-structures/graph/undirected-graph.d.ts +218 -59
- package/dist/types/data-structures/hash/hash-map.d.ts +230 -77
- package/dist/types/data-structures/heap/heap.d.ts +287 -99
- package/dist/types/data-structures/heap/max-heap.d.ts +46 -0
- package/dist/types/data-structures/heap/min-heap.d.ts +59 -0
- package/dist/types/data-structures/linked-list/doubly-linked-list.d.ts +286 -44
- package/dist/types/data-structures/linked-list/singly-linked-list.d.ts +278 -65
- package/dist/types/data-structures/linked-list/skip-linked-list.d.ts +415 -12
- package/dist/types/data-structures/matrix/matrix.d.ts +331 -0
- package/dist/types/data-structures/priority-queue/max-priority-queue.d.ts +57 -0
- package/dist/types/data-structures/priority-queue/min-priority-queue.d.ts +60 -0
- package/dist/types/data-structures/priority-queue/priority-queue.d.ts +60 -0
- package/dist/types/data-structures/queue/deque.d.ts +313 -66
- package/dist/types/data-structures/queue/queue.d.ts +211 -42
- package/dist/types/data-structures/stack/stack.d.ts +174 -32
- package/dist/types/data-structures/trie/trie.d.ts +213 -43
- package/dist/types/types/data-structures/binary-tree/segment-tree.d.ts +1 -1
- package/dist/types/types/data-structures/linked-list/skip-linked-list.d.ts +1 -4
- package/dist/types/types/data-structures/queue/deque.d.ts +6 -0
- package/dist/umd/graph-typed.js +1323 -217
- package/dist/umd/graph-typed.js.map +1 -1
- package/dist/umd/graph-typed.min.js +3 -1
- package/dist/umd/graph-typed.min.js.map +1 -1
- package/package.json +2 -2
- package/src/common/error.ts +60 -0
- package/src/common/index.ts +2 -0
- package/src/data-structures/base/iterable-element-base.ts +2 -2
- package/src/data-structures/binary-tree/avl-tree.ts +134 -51
- package/src/data-structures/binary-tree/binary-indexed-tree.ts +303 -247
- package/src/data-structures/binary-tree/binary-tree.ts +542 -121
- package/src/data-structures/binary-tree/bst.ts +346 -37
- package/src/data-structures/binary-tree/red-black-tree.ts +309 -96
- package/src/data-structures/binary-tree/segment-tree.ts +372 -248
- package/src/data-structures/binary-tree/tree-map.ts +1292 -13
- package/src/data-structures/binary-tree/tree-multi-map.ts +1098 -215
- package/src/data-structures/binary-tree/tree-multi-set.ts +863 -69
- package/src/data-structures/binary-tree/tree-set.ts +1143 -15
- package/src/data-structures/graph/abstract-graph.ts +106 -1
- package/src/data-structures/graph/directed-graph.ts +223 -47
- package/src/data-structures/graph/map-graph.ts +59 -1
- package/src/data-structures/graph/undirected-graph.ts +299 -59
- package/src/data-structures/hash/hash-map.ts +243 -79
- package/src/data-structures/heap/heap.ts +291 -102
- package/src/data-structures/heap/max-heap.ts +48 -3
- package/src/data-structures/heap/min-heap.ts +59 -0
- package/src/data-structures/linked-list/doubly-linked-list.ts +286 -44
- package/src/data-structures/linked-list/singly-linked-list.ts +278 -65
- package/src/data-structures/linked-list/skip-linked-list.ts +689 -90
- package/src/data-structures/matrix/matrix.ts +425 -22
- package/src/data-structures/priority-queue/max-priority-queue.ts +59 -3
- package/src/data-structures/priority-queue/min-priority-queue.ts +60 -0
- package/src/data-structures/priority-queue/priority-queue.ts +60 -0
- package/src/data-structures/queue/deque.ts +343 -68
- package/src/data-structures/queue/queue.ts +211 -42
- package/src/data-structures/stack/stack.ts +174 -32
- package/src/data-structures/trie/trie.ts +215 -44
- package/src/types/data-structures/binary-tree/segment-tree.ts +1 -1
- package/src/types/data-structures/linked-list/skip-linked-list.ts +2 -1
- package/src/types/data-structures/queue/deque.ts +7 -0
- package/src/utils/utils.ts +4 -2
|
@@ -7,21 +7,10 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { Comparator, TreeMultiMapOptions } from '../../types';
|
|
10
|
-
import { Range } from '../../common';
|
|
10
|
+
import { ERR, Range } from '../../common';
|
|
11
11
|
import { RedBlackTree, RedBlackTreeNode } from './red-black-tree';
|
|
12
12
|
import { TreeSet } from './tree-set';
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
* Node type used by TreeMultiMap (alias to RedBlackTreeNode for backward compatibility).
|
|
16
|
-
*
|
|
17
|
-
* @deprecated Direct node manipulation is discouraged. Use TreeMultiMap methods instead.
|
|
18
|
-
*/
|
|
19
|
-
export class TreeMultiMapNode<K = any, V = any> extends RedBlackTreeNode<K, V[]> {
|
|
20
|
-
constructor(key: K, value: V[] = []) {
|
|
21
|
-
super(key, value);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
14
|
/**
|
|
26
15
|
* TreeMultiMap (ordered MultiMap) — key → bucket (Array of values).
|
|
27
16
|
*
|
|
@@ -31,169 +20,10 @@ export class TreeMultiMapNode<K = any, V = any> extends RedBlackTreeNode<K, V[]>
|
|
|
31
20
|
* - Default iteration yields bucket entries: `[K, V[]]`.
|
|
32
21
|
* - Navigable operations (`first/last/ceiling/...`) return entry tuples like TreeMap.
|
|
33
22
|
* @example
|
|
34
|
-
* //
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* level: number;
|
|
39
|
-
* };
|
|
40
|
-
*
|
|
41
|
-
* type Player = {
|
|
42
|
-
* name: string;
|
|
43
|
-
* score: number;
|
|
44
|
-
* equipments: Equipment[];
|
|
45
|
-
* };
|
|
46
|
-
*
|
|
47
|
-
* // Mock player data with their scores and equipment
|
|
48
|
-
* const players: Player[] = [
|
|
49
|
-
* {
|
|
50
|
-
* name: 'DragonSlayer',
|
|
51
|
-
* score: 8750,
|
|
52
|
-
* equipments: [
|
|
53
|
-
* { name: 'AWM', quality: 'legendary', level: 85 },
|
|
54
|
-
* { name: 'Level 3 Helmet', quality: 'epic', level: 80 },
|
|
55
|
-
* { name: 'Extended Quickdraw Mag', quality: 'rare', level: 75 },
|
|
56
|
-
* { name: 'Compensator', quality: 'epic', level: 78 },
|
|
57
|
-
* { name: 'Vertical Grip', quality: 'rare', level: 72 }
|
|
58
|
-
* ]
|
|
59
|
-
* },
|
|
60
|
-
* {
|
|
61
|
-
* name: 'ShadowNinja',
|
|
62
|
-
* score: 7200,
|
|
63
|
-
* equipments: [
|
|
64
|
-
* { name: 'M416', quality: 'epic', level: 75 },
|
|
65
|
-
* { name: 'Ghillie Suit', quality: 'rare', level: 70 },
|
|
66
|
-
* { name: 'Red Dot Sight', quality: 'common', level: 65 },
|
|
67
|
-
* { name: 'Extended QuickDraw Mag', quality: 'rare', level: 68 }
|
|
68
|
-
* ]
|
|
69
|
-
* },
|
|
70
|
-
* {
|
|
71
|
-
* name: 'RuneMaster',
|
|
72
|
-
* score: 9100,
|
|
73
|
-
* equipments: [
|
|
74
|
-
* { name: 'KAR98K', quality: 'legendary', level: 90 },
|
|
75
|
-
* { name: 'Level 3 Vest', quality: 'legendary', level: 85 },
|
|
76
|
-
* { name: 'Holographic Sight', quality: 'epic', level: 82 },
|
|
77
|
-
* { name: 'Suppressor', quality: 'legendary', level: 88 },
|
|
78
|
-
* { name: 'Level 3 Backpack', quality: 'epic', level: 80 }
|
|
79
|
-
* ]
|
|
80
|
-
* },
|
|
81
|
-
* {
|
|
82
|
-
* name: 'BattleKing',
|
|
83
|
-
* score: 8500,
|
|
84
|
-
* equipments: [
|
|
85
|
-
* { name: 'AUG', quality: 'epic', level: 82 },
|
|
86
|
-
* { name: 'Red Dot Sight', quality: 'rare', level: 75 },
|
|
87
|
-
* { name: 'Extended Mag', quality: 'common', level: 70 },
|
|
88
|
-
* { name: 'Tactical Stock', quality: 'rare', level: 76 }
|
|
89
|
-
* ]
|
|
90
|
-
* },
|
|
91
|
-
* {
|
|
92
|
-
* name: 'SniperElite',
|
|
93
|
-
* score: 7800,
|
|
94
|
-
* equipments: [
|
|
95
|
-
* { name: 'M24', quality: 'legendary', level: 88 },
|
|
96
|
-
* { name: 'Compensator', quality: 'epic', level: 80 },
|
|
97
|
-
* { name: 'Scope 8x', quality: 'legendary', level: 85 },
|
|
98
|
-
* { name: 'Level 2 Helmet', quality: 'rare', level: 75 }
|
|
99
|
-
* ]
|
|
100
|
-
* },
|
|
101
|
-
* {
|
|
102
|
-
* name: 'RushMaster',
|
|
103
|
-
* score: 7500,
|
|
104
|
-
* equipments: [
|
|
105
|
-
* { name: 'Vector', quality: 'rare', level: 72 },
|
|
106
|
-
* { name: 'Level 2 Helmet', quality: 'common', level: 65 },
|
|
107
|
-
* { name: 'Quickdraw Mag', quality: 'common', level: 60 },
|
|
108
|
-
* { name: 'Laser Sight', quality: 'rare', level: 68 }
|
|
109
|
-
* ]
|
|
110
|
-
* },
|
|
111
|
-
* {
|
|
112
|
-
* name: 'GhostWarrior',
|
|
113
|
-
* score: 8200,
|
|
114
|
-
* equipments: [
|
|
115
|
-
* { name: 'SCAR-L', quality: 'epic', level: 78 },
|
|
116
|
-
* { name: 'Extended Quickdraw Mag', quality: 'rare', level: 70 },
|
|
117
|
-
* { name: 'Holographic Sight', quality: 'epic', level: 75 },
|
|
118
|
-
* { name: 'Suppressor', quality: 'rare', level: 72 },
|
|
119
|
-
* { name: 'Vertical Grip', quality: 'common', level: 65 }
|
|
120
|
-
* ]
|
|
121
|
-
* },
|
|
122
|
-
* {
|
|
123
|
-
* name: 'DeathDealer',
|
|
124
|
-
* score: 7300,
|
|
125
|
-
* equipments: [
|
|
126
|
-
* { name: 'SKS', quality: 'epic', level: 76 },
|
|
127
|
-
* { name: 'Holographic Sight', quality: 'rare', level: 68 },
|
|
128
|
-
* { name: 'Extended Mag', quality: 'common', level: 65 }
|
|
129
|
-
* ]
|
|
130
|
-
* },
|
|
131
|
-
* {
|
|
132
|
-
* name: 'StormRider',
|
|
133
|
-
* score: 8900,
|
|
134
|
-
* equipments: [
|
|
135
|
-
* { name: 'MK14', quality: 'legendary', level: 92 },
|
|
136
|
-
* { name: 'Level 3 Backpack', quality: 'legendary', level: 85 },
|
|
137
|
-
* { name: 'Scope 8x', quality: 'epic', level: 80 },
|
|
138
|
-
* { name: 'Suppressor', quality: 'legendary', level: 88 },
|
|
139
|
-
* { name: 'Tactical Stock', quality: 'rare', level: 75 }
|
|
140
|
-
* ]
|
|
141
|
-
* },
|
|
142
|
-
* {
|
|
143
|
-
* name: 'CombatLegend',
|
|
144
|
-
* score: 7600,
|
|
145
|
-
* equipments: [
|
|
146
|
-
* { name: 'UMP45', quality: 'rare', level: 74 },
|
|
147
|
-
* { name: 'Level 2 Vest', quality: 'common', level: 67 },
|
|
148
|
-
* { name: 'Red Dot Sight', quality: 'common', level: 62 },
|
|
149
|
-
* { name: 'Extended Mag', quality: 'rare', level: 70 }
|
|
150
|
-
* ]
|
|
151
|
-
* }
|
|
152
|
-
* ];
|
|
153
|
-
*
|
|
154
|
-
* // Create a TreeMultiMap for player rankings
|
|
155
|
-
* const playerRankings = new TreeMultiMap<number, Equipment, Player>(players, {
|
|
156
|
-
* toEntryFn: ({ score, equipments }) => [score, equipments],
|
|
157
|
-
* isMapMode: false
|
|
158
|
-
* });
|
|
159
|
-
*
|
|
160
|
-
* const topPlayersEquipments = playerRankings.rangeSearch([8900, 10000], node => playerRankings.get(node.key));
|
|
161
|
-
* console.log(topPlayersEquipments); // [
|
|
162
|
-
* // [
|
|
163
|
-
* // {
|
|
164
|
-
* // name: 'MK14',
|
|
165
|
-
* // quality: 'legendary',
|
|
166
|
-
* // level: 92
|
|
167
|
-
* // },
|
|
168
|
-
* // { name: 'Level 3 Backpack', quality: 'legendary', level: 85 },
|
|
169
|
-
* // {
|
|
170
|
-
* // name: 'Scope 8x',
|
|
171
|
-
* // quality: 'epic',
|
|
172
|
-
* // level: 80
|
|
173
|
-
* // },
|
|
174
|
-
* // { name: 'Suppressor', quality: 'legendary', level: 88 },
|
|
175
|
-
* // {
|
|
176
|
-
* // name: 'Tactical Stock',
|
|
177
|
-
* // quality: 'rare',
|
|
178
|
-
* // level: 75
|
|
179
|
-
* // }
|
|
180
|
-
* // ],
|
|
181
|
-
* // [
|
|
182
|
-
* // { name: 'KAR98K', quality: 'legendary', level: 90 },
|
|
183
|
-
* // {
|
|
184
|
-
* // name: 'Level 3 Vest',
|
|
185
|
-
* // quality: 'legendary',
|
|
186
|
-
* // level: 85
|
|
187
|
-
* // },
|
|
188
|
-
* // { name: 'Holographic Sight', quality: 'epic', level: 82 },
|
|
189
|
-
* // {
|
|
190
|
-
* // name: 'Suppressor',
|
|
191
|
-
* // quality: 'legendary',
|
|
192
|
-
* // level: 88
|
|
193
|
-
* // },
|
|
194
|
-
* // { name: 'Level 3 Backpack', quality: 'epic', level: 80 }
|
|
195
|
-
* // ]
|
|
196
|
-
* // ];
|
|
23
|
+
* // Morris traversal (O(1) space)
|
|
24
|
+
* const tmm = new TreeMultiMap<number>([5, 3, 7]);
|
|
25
|
+
* const result = tmm.morris(n => n.key, 'IN');
|
|
26
|
+
* console.log(result.length); // > 0;
|
|
197
27
|
*/
|
|
198
28
|
export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]]> {
|
|
199
29
|
readonly #core: RedBlackTree<K, V[], R>;
|
|
@@ -261,15 +91,15 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
261
91
|
// reuse TreeSet strict validation (same policy)
|
|
262
92
|
// NOTE: TreeSet._validateKey is private, so we replicate the checks.
|
|
263
93
|
if (typeof key === 'number') {
|
|
264
|
-
if (Number.isNaN(key)) throw new TypeError('TreeMultiMap
|
|
94
|
+
if (Number.isNaN(key)) throw new TypeError(ERR.invalidNaN('TreeMultiMap'));
|
|
265
95
|
return;
|
|
266
96
|
}
|
|
267
97
|
if (typeof key === 'string') return;
|
|
268
98
|
if (key instanceof Date) {
|
|
269
|
-
if (Number.isNaN(key.getTime())) throw new TypeError('TreeMultiMap
|
|
99
|
+
if (Number.isNaN(key.getTime())) throw new TypeError(ERR.invalidDate('TreeMultiMap'));
|
|
270
100
|
return;
|
|
271
101
|
}
|
|
272
|
-
throw new TypeError('TreeMultiMap
|
|
102
|
+
throw new TypeError(ERR.comparatorRequired('TreeMultiMap'));
|
|
273
103
|
}
|
|
274
104
|
|
|
275
105
|
/**
|
|
@@ -283,6 +113,47 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
283
113
|
/**
|
|
284
114
|
* Whether the map is empty.
|
|
285
115
|
* @remarks Time O(1), Space O(1)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
* @example
|
|
155
|
+
* // Check if empty
|
|
156
|
+
* console.log(new TreeMultiMap().isEmpty()); // true;
|
|
286
157
|
*/
|
|
287
158
|
isEmpty(): boolean {
|
|
288
159
|
return this.size === 0;
|
|
@@ -291,6 +162,50 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
291
162
|
/**
|
|
292
163
|
* Removes all entries from the map.
|
|
293
164
|
* @remarks Time O(1), Space O(1)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
* @example
|
|
204
|
+
* // Remove all entries
|
|
205
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
206
|
+
* mm.add(1, 'a');
|
|
207
|
+
* mm.clear();
|
|
208
|
+
* console.log(mm.isEmpty()); // true;
|
|
294
209
|
*/
|
|
295
210
|
clear(): void {
|
|
296
211
|
this.#core.clear();
|
|
@@ -299,6 +214,14 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
299
214
|
/**
|
|
300
215
|
* Bucket length for a key (missing => 0).
|
|
301
216
|
* @remarks Time O(log n), Space O(1)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
* @example
|
|
220
|
+
* // Count values for key
|
|
221
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
222
|
+
* mm.add(1, 'a');
|
|
223
|
+
* mm.add(1, 'b');
|
|
224
|
+
* console.log(mm.count(1)); // 2;
|
|
302
225
|
*/
|
|
303
226
|
count(key: K): number {
|
|
304
227
|
const b = this.get(key);
|
|
@@ -308,6 +231,15 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
308
231
|
/**
|
|
309
232
|
* Total number of values across all buckets (Σ bucket.length).
|
|
310
233
|
* @remarks Time O(n), Space O(1)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
* @example
|
|
237
|
+
* // Total number of values
|
|
238
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
239
|
+
* mm.add(1, 'a');
|
|
240
|
+
* mm.add(1, 'b');
|
|
241
|
+
* mm.add(2, 'c');
|
|
242
|
+
* console.log(mm.totalSize); // 3;
|
|
311
243
|
*/
|
|
312
244
|
get totalSize(): number {
|
|
313
245
|
let sum = 0;
|
|
@@ -318,6 +250,64 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
318
250
|
/**
|
|
319
251
|
* Whether the map contains the given key.
|
|
320
252
|
* @remarks Time O(log n), Space O(1)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
* @example
|
|
306
|
+
* // Check key existence
|
|
307
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
308
|
+
* mm.add(1, 'a');
|
|
309
|
+
* console.log(mm.has(1)); // true;
|
|
310
|
+
* console.log(mm.has(2)); // false;
|
|
321
311
|
*/
|
|
322
312
|
has(key: K): boolean {
|
|
323
313
|
this._validateKey(key);
|
|
@@ -327,6 +317,64 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
327
317
|
/**
|
|
328
318
|
* Live bucket reference (do not auto-delete key if bucket becomes empty via mutation).
|
|
329
319
|
* @remarks Time O(log n), Space O(1)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
* @example
|
|
373
|
+
* // Get values for key
|
|
374
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
375
|
+
* mm.add(1, 'a');
|
|
376
|
+
* mm.add(1, 'b');
|
|
377
|
+
* console.log(mm.get(1)); // ['a', 'b'];
|
|
330
378
|
*/
|
|
331
379
|
get(key: K): V[] | undefined {
|
|
332
380
|
this._validateKey(key);
|
|
@@ -336,6 +384,45 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
336
384
|
/**
|
|
337
385
|
* Append a single value.
|
|
338
386
|
* @remarks Time O(log n), Space O(1)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
* @example
|
|
420
|
+
* // Add key-value pair
|
|
421
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
422
|
+
* mm.add(1, 'a');
|
|
423
|
+
* mm.add(1, 'b');
|
|
424
|
+
* mm.add(2, 'c');
|
|
425
|
+
* console.log(mm.get(1)); // ['a', 'b'];
|
|
339
426
|
*/
|
|
340
427
|
add(key: K, value: V): boolean {
|
|
341
428
|
this._validateKey(key);
|
|
@@ -350,6 +437,62 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
350
437
|
/**
|
|
351
438
|
* Alias for compatibility with existing TreeMultiMap semantics.
|
|
352
439
|
* @remarks Time O(log n), Space O(1) for single value; O(log n + m) for bucket append
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
* @example
|
|
491
|
+
* // Set values for key
|
|
492
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
493
|
+
* mm.set(1, 'a');
|
|
494
|
+
* mm.set(1, 'b');
|
|
495
|
+
* console.log(mm.get(1)); // ['a', 'b'];
|
|
353
496
|
*/
|
|
354
497
|
set(entry: [K | null | undefined, V[] | undefined] | K | null | undefined, value?: V): boolean;
|
|
355
498
|
set(key: K, value: V): boolean;
|
|
@@ -379,6 +522,65 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
379
522
|
/**
|
|
380
523
|
* Deletes a key and its entire bucket.
|
|
381
524
|
* @remarks Time O(log n), Space O(1)
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
* @example
|
|
578
|
+
* // Remove key
|
|
579
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
580
|
+
* mm.add(1, 'a');
|
|
581
|
+
* mm.add(2, 'b');
|
|
582
|
+
* mm.delete(1);
|
|
583
|
+
* console.log(mm.has(1)); // false;
|
|
382
584
|
*/
|
|
383
585
|
delete(key: K): boolean {
|
|
384
586
|
this._validateKey(key);
|
|
@@ -388,6 +590,14 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
388
590
|
/**
|
|
389
591
|
* Check if a specific value exists in a key's bucket.
|
|
390
592
|
* @remarks Time O(log n + m), Space O(1) where m is bucket size
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
* @example
|
|
596
|
+
* // Check specific key-value
|
|
597
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
598
|
+
* mm.add(1, 'a');
|
|
599
|
+
* console.log(mm.hasEntry(1, 'a')); // true;
|
|
600
|
+
* console.log(mm.hasEntry(1, 'z')); // false;
|
|
391
601
|
*/
|
|
392
602
|
hasEntry(key: K, value: V, eq: (a: V, b: V) => boolean = Object.is): boolean {
|
|
393
603
|
const bucket = this.get(key);
|
|
@@ -398,6 +608,15 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
398
608
|
/**
|
|
399
609
|
* Delete a single occurrence of a value from a key's bucket.
|
|
400
610
|
* @remarks Time O(log n + m), Space O(1) where m is bucket size
|
|
611
|
+
|
|
612
|
+
|
|
613
|
+
* @example
|
|
614
|
+
* // Delete specific value
|
|
615
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
616
|
+
* mm.add(1, 'a');
|
|
617
|
+
* mm.add(1, 'b');
|
|
618
|
+
* mm.deleteValue(1, 'a');
|
|
619
|
+
* console.log(mm.get(1)); // ['b'];
|
|
401
620
|
*/
|
|
402
621
|
deleteValue(key: K, value: V, eq: (a: V, b: V) => boolean = Object.is): boolean {
|
|
403
622
|
const bucket = this.get(key);
|
|
@@ -412,6 +631,16 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
412
631
|
/**
|
|
413
632
|
* Delete all occurrences of a value from a key's bucket.
|
|
414
633
|
* @remarks Time O(log n + m), Space O(1) where m is bucket size
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
* @example
|
|
637
|
+
* // Delete all matching values
|
|
638
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
639
|
+
* mm.add(1, 'a');
|
|
640
|
+
* mm.add(1, 'a');
|
|
641
|
+
* mm.add(1, 'b');
|
|
642
|
+
* const count = mm.deleteValues(1, 'a');
|
|
643
|
+
* console.log(count); // 2;
|
|
415
644
|
*/
|
|
416
645
|
deleteValues(key: K, value: V, eq: (a: V, b: V) => boolean = Object.is): number {
|
|
417
646
|
const bucket = this.get(key);
|
|
@@ -436,13 +665,57 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
436
665
|
*[Symbol.iterator](): Iterator<[K, V[]]> {
|
|
437
666
|
for (const [k, v] of this.#core) {
|
|
438
667
|
// core always stores buckets, but guard anyway
|
|
439
|
-
yield [k, v ?? ([] as V[])];
|
|
668
|
+
yield [k, v ?? /* istanbul ignore next */ ([] as V[])];
|
|
440
669
|
}
|
|
441
670
|
}
|
|
442
671
|
|
|
443
672
|
/**
|
|
444
673
|
* Iterates over all keys.
|
|
445
674
|
* @remarks Time O(n), Space O(1)
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
* @example
|
|
714
|
+
* // Iterate keys
|
|
715
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
716
|
+
* mm.add(3, 'c');
|
|
717
|
+
* mm.add(1, 'a');
|
|
718
|
+
* console.log([...mm.keys()]); // [1, 3];
|
|
446
719
|
*/
|
|
447
720
|
*keys(): IterableIterator<K> {
|
|
448
721
|
yield* this.#core.keys();
|
|
@@ -451,6 +724,50 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
451
724
|
/**
|
|
452
725
|
* Iterates over all buckets.
|
|
453
726
|
* @remarks Time O(n), Space O(1)
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
* @example
|
|
766
|
+
* // Iterate value arrays
|
|
767
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
768
|
+
* mm.add(1, 'a');
|
|
769
|
+
* mm.add(1, 'b');
|
|
770
|
+
* console.log([...mm.values()]); // [['a', 'b']];
|
|
454
771
|
*/
|
|
455
772
|
*values(): IterableIterator<V[]> {
|
|
456
773
|
for (const [, bucket] of this) yield bucket;
|
|
@@ -461,6 +778,14 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
461
778
|
/**
|
|
462
779
|
* Iterates over all entries for a specific key.
|
|
463
780
|
* @remarks Time O(log n + m), Space O(1) where m is bucket size
|
|
781
|
+
|
|
782
|
+
|
|
783
|
+
* @example
|
|
784
|
+
* // Get entries for key
|
|
785
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
786
|
+
* mm.add(1, 'a');
|
|
787
|
+
* mm.add(1, 'b');
|
|
788
|
+
* console.log([...mm.entriesOf(1)]); // [[1, 'a'], [1, 'b']];
|
|
464
789
|
*/
|
|
465
790
|
*entriesOf(key: K): IterableIterator<[K, V]> {
|
|
466
791
|
const bucket = this.get(key);
|
|
@@ -471,6 +796,14 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
471
796
|
/**
|
|
472
797
|
* Iterates over all values for a specific key.
|
|
473
798
|
* @remarks Time O(log n + m), Space O(1) where m is bucket size
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
* @example
|
|
802
|
+
* // Get flat values for key
|
|
803
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
804
|
+
* mm.add(1, 'a');
|
|
805
|
+
* mm.add(1, 'b');
|
|
806
|
+
* console.log([...mm.valuesOf(1)]); // ['a', 'b'];
|
|
474
807
|
*/
|
|
475
808
|
*valuesOf(key: K): IterableIterator<V> {
|
|
476
809
|
const bucket = this.get(key);
|
|
@@ -481,6 +814,15 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
481
814
|
/**
|
|
482
815
|
* Iterates over all [key, value] pairs (flattened from buckets).
|
|
483
816
|
* @remarks Time O(T), Space O(1) where T is totalSize
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
* @example
|
|
820
|
+
* // All key-value pairs flattened
|
|
821
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
822
|
+
* mm.add(1, 'a');
|
|
823
|
+
* mm.add(1, 'b');
|
|
824
|
+
* mm.add(2, 'c');
|
|
825
|
+
* console.log([...mm.flatEntries()]); // [[1, 'a'], [1, 'b'], [2, 'c']];
|
|
484
826
|
*/
|
|
485
827
|
*flatEntries(): IterableIterator<[K, V]> {
|
|
486
828
|
for (const [k, bucket] of this) {
|
|
@@ -493,38 +835,79 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
493
835
|
/**
|
|
494
836
|
* Returns the entry with the smallest key.
|
|
495
837
|
* @remarks Time O(log n), Space O(1)
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
838
|
+
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
* @example
|
|
853
|
+
* // First entry
|
|
854
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
855
|
+
* mm.add(3, 'c');
|
|
856
|
+
* mm.add(1, 'a');
|
|
857
|
+
* console.log(mm.first()?.[0]); // 1;
|
|
499
858
|
*/
|
|
500
859
|
first(): [K, V[]] | undefined {
|
|
501
860
|
const k = this.#core.getLeftMost();
|
|
502
861
|
if (k === undefined) return undefined;
|
|
503
862
|
const b = this.get(k);
|
|
504
|
-
return b === undefined ? undefined : [k, b];
|
|
863
|
+
return b === undefined ? /* istanbul ignore next -- defensive: key in core always has bucket */ undefined : [k, b];
|
|
505
864
|
}
|
|
506
865
|
|
|
507
866
|
/**
|
|
508
867
|
* Returns the entry with the largest key.
|
|
509
868
|
* @remarks Time O(log n), Space O(1)
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
869
|
+
|
|
870
|
+
|
|
871
|
+
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
|
|
883
|
+
* @example
|
|
884
|
+
* // Last entry
|
|
885
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
886
|
+
* mm.add(1, 'a');
|
|
887
|
+
* mm.add(3, 'c');
|
|
888
|
+
* console.log(mm.last()?.[0]); // 3;
|
|
513
889
|
*/
|
|
514
890
|
last(): [K, V[]] | undefined {
|
|
515
891
|
const k = this.#core.getRightMost();
|
|
516
892
|
if (k === undefined) return undefined;
|
|
517
893
|
const b = this.get(k);
|
|
518
|
-
return b === undefined ? undefined : [k, b];
|
|
894
|
+
return b === undefined ? /* istanbul ignore next -- defensive: key in core always has bucket */ undefined : [k, b];
|
|
519
895
|
}
|
|
520
896
|
|
|
521
897
|
/**
|
|
522
898
|
* Removes and returns the entry with the smallest key.
|
|
523
899
|
* @remarks Time O(log n), Space O(1)
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
900
|
+
|
|
901
|
+
|
|
902
|
+
|
|
903
|
+
* @example
|
|
904
|
+
* // Remove and return first
|
|
905
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
906
|
+
* mm.add(2, 'b');
|
|
907
|
+
* mm.add(1, 'a');
|
|
908
|
+
* const first = mm.pollFirst();
|
|
909
|
+
* console.log(first?.[0]); // 1;
|
|
910
|
+
* console.log(mm.has(1)); // false;
|
|
528
911
|
*/
|
|
529
912
|
pollFirst(): [K, V[]] | undefined {
|
|
530
913
|
const e = this.first();
|
|
@@ -536,10 +919,16 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
536
919
|
/**
|
|
537
920
|
* Removes and returns the entry with the largest key.
|
|
538
921
|
* @remarks Time O(log n), Space O(1)
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
|
|
925
|
+
* @example
|
|
926
|
+
* // Remove and return last
|
|
927
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
928
|
+
* mm.add(1, 'a');
|
|
929
|
+
* mm.add(3, 'c');
|
|
930
|
+
* const last = mm.pollLast();
|
|
931
|
+
* console.log(last?.[0]); // 3;
|
|
543
932
|
*/
|
|
544
933
|
pollLast(): [K, V[]] | undefined {
|
|
545
934
|
const e = this.last();
|
|
@@ -551,65 +940,217 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
551
940
|
/**
|
|
552
941
|
* Returns the entry with the smallest key >= given key.
|
|
553
942
|
* @remarks Time O(log n), Space O(1)
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
943
|
+
|
|
944
|
+
|
|
945
|
+
|
|
946
|
+
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
|
|
953
|
+
|
|
954
|
+
|
|
955
|
+
|
|
956
|
+
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
|
|
962
|
+
|
|
963
|
+
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
|
|
971
|
+
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+
|
|
983
|
+
|
|
984
|
+
* @example
|
|
985
|
+
* // Least key ≥ target
|
|
986
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
987
|
+
* mm.add(10, 'a');
|
|
988
|
+
* mm.add(20, 'b');
|
|
989
|
+
* mm.add(30, 'c');
|
|
990
|
+
* console.log(mm.ceiling(15)?.[0]); // 20;
|
|
558
991
|
*/
|
|
559
992
|
ceiling(key: K): [K, V[]] | undefined {
|
|
560
993
|
this._validateKey(key);
|
|
561
994
|
const k = this.#core.ceiling(key);
|
|
562
995
|
if (k === undefined) return undefined;
|
|
563
996
|
const b = this.get(k);
|
|
564
|
-
return b === undefined ? undefined : [k, b];
|
|
997
|
+
return b === undefined ? /* istanbul ignore next -- defensive: key in core always has bucket */ undefined : [k, b];
|
|
565
998
|
}
|
|
566
999
|
|
|
567
1000
|
/**
|
|
568
1001
|
* Returns the entry with the largest key <= given key.
|
|
569
1002
|
* @remarks Time O(log n), Space O(1)
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
1003
|
+
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
|
|
1007
|
+
|
|
1008
|
+
|
|
1009
|
+
|
|
1010
|
+
|
|
1011
|
+
|
|
1012
|
+
|
|
1013
|
+
|
|
1014
|
+
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
|
|
1022
|
+
|
|
1023
|
+
|
|
1024
|
+
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
|
|
1032
|
+
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
|
|
1036
|
+
|
|
1037
|
+
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
|
|
1041
|
+
|
|
1042
|
+
|
|
1043
|
+
|
|
1044
|
+
* @example
|
|
1045
|
+
* // Greatest key ≤ target
|
|
1046
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
1047
|
+
* mm.add(10, 'a');
|
|
1048
|
+
* mm.add(20, 'b');
|
|
1049
|
+
* mm.add(30, 'c');
|
|
1050
|
+
* console.log(mm.floor(25)?.[0]); // 20;
|
|
574
1051
|
*/
|
|
575
1052
|
floor(key: K): [K, V[]] | undefined {
|
|
576
1053
|
this._validateKey(key);
|
|
577
1054
|
const k = this.#core.floor(key);
|
|
578
1055
|
if (k === undefined) return undefined;
|
|
579
1056
|
const b = this.get(k);
|
|
580
|
-
return b === undefined ? undefined : [k, b];
|
|
1057
|
+
return b === undefined ? /* istanbul ignore next -- defensive: key in core always has bucket */ undefined : [k, b];
|
|
581
1058
|
}
|
|
582
1059
|
|
|
583
1060
|
/**
|
|
584
1061
|
* Returns the entry with the smallest key > given key.
|
|
585
1062
|
* @remarks Time O(log n), Space O(1)
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
1063
|
+
|
|
1064
|
+
|
|
1065
|
+
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
|
|
1069
|
+
|
|
1070
|
+
|
|
1071
|
+
|
|
1072
|
+
|
|
1073
|
+
|
|
1074
|
+
|
|
1075
|
+
|
|
1076
|
+
|
|
1077
|
+
|
|
1078
|
+
|
|
1079
|
+
|
|
1080
|
+
|
|
1081
|
+
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
|
|
1085
|
+
|
|
1086
|
+
|
|
1087
|
+
|
|
1088
|
+
|
|
1089
|
+
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
|
|
1093
|
+
* @example
|
|
1094
|
+
* // Least key > target
|
|
1095
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
1096
|
+
* mm.add(10, 'a');
|
|
1097
|
+
* mm.add(20, 'b');
|
|
1098
|
+
* console.log(mm.higher(10)?.[0]); // 20;
|
|
590
1099
|
*/
|
|
591
1100
|
higher(key: K): [K, V[]] | undefined {
|
|
592
1101
|
this._validateKey(key);
|
|
593
1102
|
const k = this.#core.higher(key);
|
|
594
1103
|
if (k === undefined) return undefined;
|
|
595
1104
|
const b = this.get(k);
|
|
596
|
-
return b === undefined ? undefined : [k, b];
|
|
1105
|
+
return b === undefined ? /* istanbul ignore next -- defensive: key in core always has bucket */ undefined : [k, b];
|
|
597
1106
|
}
|
|
598
1107
|
|
|
599
1108
|
/**
|
|
600
1109
|
* Returns the entry with the largest key < given key.
|
|
601
1110
|
* @remarks Time O(log n), Space O(1)
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
1111
|
+
|
|
1112
|
+
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
|
|
1116
|
+
|
|
1117
|
+
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
|
|
1121
|
+
|
|
1122
|
+
|
|
1123
|
+
|
|
1124
|
+
|
|
1125
|
+
|
|
1126
|
+
|
|
1127
|
+
|
|
1128
|
+
|
|
1129
|
+
|
|
1130
|
+
|
|
1131
|
+
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
|
|
1135
|
+
|
|
1136
|
+
|
|
1137
|
+
|
|
1138
|
+
|
|
1139
|
+
|
|
1140
|
+
|
|
1141
|
+
* @example
|
|
1142
|
+
* // Greatest key < target
|
|
1143
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
1144
|
+
* mm.add(10, 'a');
|
|
1145
|
+
* mm.add(20, 'b');
|
|
1146
|
+
* console.log(mm.lower(20)?.[0]); // 10;
|
|
606
1147
|
*/
|
|
607
1148
|
lower(key: K): [K, V[]] | undefined {
|
|
608
1149
|
this._validateKey(key);
|
|
609
1150
|
const k = this.#core.lower(key);
|
|
610
1151
|
if (k === undefined) return undefined;
|
|
611
1152
|
const b = this.get(k);
|
|
612
|
-
return b === undefined ? undefined : [k, b];
|
|
1153
|
+
return b === undefined ? /* istanbul ignore next -- defensive: key in core always has bucket */ undefined : [k, b];
|
|
613
1154
|
}
|
|
614
1155
|
|
|
615
1156
|
// ━━━ Tree utilities ━━━
|
|
@@ -617,6 +1158,49 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
617
1158
|
/**
|
|
618
1159
|
* Prints the internal tree structure (for debugging).
|
|
619
1160
|
* @remarks Time O(n), Space O(n)
|
|
1161
|
+
|
|
1162
|
+
|
|
1163
|
+
|
|
1164
|
+
|
|
1165
|
+
|
|
1166
|
+
|
|
1167
|
+
|
|
1168
|
+
|
|
1169
|
+
|
|
1170
|
+
|
|
1171
|
+
|
|
1172
|
+
|
|
1173
|
+
|
|
1174
|
+
|
|
1175
|
+
|
|
1176
|
+
|
|
1177
|
+
|
|
1178
|
+
|
|
1179
|
+
|
|
1180
|
+
|
|
1181
|
+
|
|
1182
|
+
|
|
1183
|
+
|
|
1184
|
+
|
|
1185
|
+
|
|
1186
|
+
|
|
1187
|
+
|
|
1188
|
+
|
|
1189
|
+
|
|
1190
|
+
|
|
1191
|
+
|
|
1192
|
+
|
|
1193
|
+
|
|
1194
|
+
|
|
1195
|
+
|
|
1196
|
+
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
* @example
|
|
1200
|
+
* // Display tree
|
|
1201
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
1202
|
+
* mm.add(1, 'a');
|
|
1203
|
+
* expect(() => mm.print()).not.toThrow();
|
|
620
1204
|
*/
|
|
621
1205
|
print(): void {
|
|
622
1206
|
this.#core.print();
|
|
@@ -625,6 +1209,52 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
625
1209
|
/**
|
|
626
1210
|
* Executes a callback for each entry.
|
|
627
1211
|
* @remarks Time O(n), Space O(1)
|
|
1212
|
+
|
|
1213
|
+
|
|
1214
|
+
|
|
1215
|
+
|
|
1216
|
+
|
|
1217
|
+
|
|
1218
|
+
|
|
1219
|
+
|
|
1220
|
+
|
|
1221
|
+
|
|
1222
|
+
|
|
1223
|
+
|
|
1224
|
+
|
|
1225
|
+
|
|
1226
|
+
|
|
1227
|
+
|
|
1228
|
+
|
|
1229
|
+
|
|
1230
|
+
|
|
1231
|
+
|
|
1232
|
+
|
|
1233
|
+
|
|
1234
|
+
|
|
1235
|
+
|
|
1236
|
+
|
|
1237
|
+
|
|
1238
|
+
|
|
1239
|
+
|
|
1240
|
+
|
|
1241
|
+
|
|
1242
|
+
|
|
1243
|
+
|
|
1244
|
+
|
|
1245
|
+
|
|
1246
|
+
|
|
1247
|
+
|
|
1248
|
+
|
|
1249
|
+
|
|
1250
|
+
* @example
|
|
1251
|
+
* // Iterate entries
|
|
1252
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
1253
|
+
* mm.add(1, 'a');
|
|
1254
|
+
* mm.add(2, 'b');
|
|
1255
|
+
* const keys: number[] = [];
|
|
1256
|
+
* mm.forEach((v, k) => keys.push(k));
|
|
1257
|
+
* console.log(keys); // [1, 2];
|
|
628
1258
|
*/
|
|
629
1259
|
forEach(callback: (value: V[], key: K, map: this) => void): void {
|
|
630
1260
|
for (const [k, v] of this) {
|
|
@@ -635,6 +1265,52 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
635
1265
|
/**
|
|
636
1266
|
* Creates a new map with entries that pass the predicate.
|
|
637
1267
|
* @remarks Time O(n), Space O(n)
|
|
1268
|
+
|
|
1269
|
+
|
|
1270
|
+
|
|
1271
|
+
|
|
1272
|
+
|
|
1273
|
+
|
|
1274
|
+
|
|
1275
|
+
|
|
1276
|
+
|
|
1277
|
+
|
|
1278
|
+
|
|
1279
|
+
|
|
1280
|
+
|
|
1281
|
+
|
|
1282
|
+
|
|
1283
|
+
|
|
1284
|
+
|
|
1285
|
+
|
|
1286
|
+
|
|
1287
|
+
|
|
1288
|
+
|
|
1289
|
+
|
|
1290
|
+
|
|
1291
|
+
|
|
1292
|
+
|
|
1293
|
+
|
|
1294
|
+
|
|
1295
|
+
|
|
1296
|
+
|
|
1297
|
+
|
|
1298
|
+
|
|
1299
|
+
|
|
1300
|
+
|
|
1301
|
+
|
|
1302
|
+
|
|
1303
|
+
|
|
1304
|
+
|
|
1305
|
+
|
|
1306
|
+
* @example
|
|
1307
|
+
* // Filter entries
|
|
1308
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
1309
|
+
* mm.add(1, 'a');
|
|
1310
|
+
* mm.add(2, 'b');
|
|
1311
|
+
* mm.add(3, 'c');
|
|
1312
|
+
* const filtered = mm.filter((v, k) => k > 1);
|
|
1313
|
+
* console.log([...filtered.keys()]); // [2, 3];
|
|
638
1314
|
*/
|
|
639
1315
|
filter(predicate: (value: V[], key: K, map: this) => boolean): TreeMultiMap<K, V, R> {
|
|
640
1316
|
const filtered: [K, V[]][] = [];
|
|
@@ -647,6 +1323,50 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
647
1323
|
/**
|
|
648
1324
|
* Creates a new map by transforming each entry.
|
|
649
1325
|
* @remarks Time O(n log n), Space O(n)
|
|
1326
|
+
|
|
1327
|
+
|
|
1328
|
+
|
|
1329
|
+
|
|
1330
|
+
|
|
1331
|
+
|
|
1332
|
+
|
|
1333
|
+
|
|
1334
|
+
|
|
1335
|
+
|
|
1336
|
+
|
|
1337
|
+
|
|
1338
|
+
|
|
1339
|
+
|
|
1340
|
+
|
|
1341
|
+
|
|
1342
|
+
|
|
1343
|
+
|
|
1344
|
+
|
|
1345
|
+
|
|
1346
|
+
|
|
1347
|
+
|
|
1348
|
+
|
|
1349
|
+
|
|
1350
|
+
|
|
1351
|
+
|
|
1352
|
+
|
|
1353
|
+
|
|
1354
|
+
|
|
1355
|
+
|
|
1356
|
+
|
|
1357
|
+
|
|
1358
|
+
|
|
1359
|
+
|
|
1360
|
+
|
|
1361
|
+
|
|
1362
|
+
|
|
1363
|
+
|
|
1364
|
+
* @example
|
|
1365
|
+
* // Transform values
|
|
1366
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
1367
|
+
* mm.add(1, 'a');
|
|
1368
|
+
* const mapped = mm.map((v, k) => [k, v.map(s => s.toUpperCase())] as [number, string[]]);
|
|
1369
|
+
* console.log(mapped.get(1)); // ['A'];
|
|
650
1370
|
*/
|
|
651
1371
|
map<V2>(
|
|
652
1372
|
mapper: (value: V[], key: K, map: this) => [K, V2[]]
|
|
@@ -661,6 +1381,51 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
661
1381
|
/**
|
|
662
1382
|
* Reduces all entries to a single value.
|
|
663
1383
|
* @remarks Time O(n), Space O(1)
|
|
1384
|
+
|
|
1385
|
+
|
|
1386
|
+
|
|
1387
|
+
|
|
1388
|
+
|
|
1389
|
+
|
|
1390
|
+
|
|
1391
|
+
|
|
1392
|
+
|
|
1393
|
+
|
|
1394
|
+
|
|
1395
|
+
|
|
1396
|
+
|
|
1397
|
+
|
|
1398
|
+
|
|
1399
|
+
|
|
1400
|
+
|
|
1401
|
+
|
|
1402
|
+
|
|
1403
|
+
|
|
1404
|
+
|
|
1405
|
+
|
|
1406
|
+
|
|
1407
|
+
|
|
1408
|
+
|
|
1409
|
+
|
|
1410
|
+
|
|
1411
|
+
|
|
1412
|
+
|
|
1413
|
+
|
|
1414
|
+
|
|
1415
|
+
|
|
1416
|
+
|
|
1417
|
+
|
|
1418
|
+
|
|
1419
|
+
|
|
1420
|
+
|
|
1421
|
+
|
|
1422
|
+
* @example
|
|
1423
|
+
* // Aggregate
|
|
1424
|
+
* const mm = new TreeMultiMap<number, number>();
|
|
1425
|
+
* mm.add(1, 10);
|
|
1426
|
+
* mm.add(2, 20);
|
|
1427
|
+
* const sum = mm.reduce((acc, v) => acc + v.reduce((a, b) => a + b, 0), 0);
|
|
1428
|
+
* console.log(sum); // 30;
|
|
664
1429
|
*/
|
|
665
1430
|
reduce<U>(callback: (accumulator: U, value: V[], key: K, map: this) => U, initialValue: U): U {
|
|
666
1431
|
let acc = initialValue;
|
|
@@ -673,6 +1438,41 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
673
1438
|
/**
|
|
674
1439
|
* Sets multiple entries at once.
|
|
675
1440
|
* @remarks Time O(m log n), Space O(m) where m is input size
|
|
1441
|
+
|
|
1442
|
+
|
|
1443
|
+
|
|
1444
|
+
|
|
1445
|
+
|
|
1446
|
+
|
|
1447
|
+
|
|
1448
|
+
|
|
1449
|
+
|
|
1450
|
+
|
|
1451
|
+
|
|
1452
|
+
|
|
1453
|
+
|
|
1454
|
+
|
|
1455
|
+
|
|
1456
|
+
|
|
1457
|
+
|
|
1458
|
+
|
|
1459
|
+
|
|
1460
|
+
|
|
1461
|
+
|
|
1462
|
+
|
|
1463
|
+
|
|
1464
|
+
|
|
1465
|
+
|
|
1466
|
+
|
|
1467
|
+
|
|
1468
|
+
|
|
1469
|
+
|
|
1470
|
+
|
|
1471
|
+
* @example
|
|
1472
|
+
* // Set multiple entries
|
|
1473
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
1474
|
+
* mm.setMany([[1, ['a']], [2, ['b']]]);
|
|
1475
|
+
* console.log(mm.size); // 2;
|
|
676
1476
|
*/
|
|
677
1477
|
setMany(keysNodesEntriesOrRaws: Iterable<K | [K | null | undefined, V[] | undefined]>): boolean[] {
|
|
678
1478
|
const results: boolean[] = [];
|
|
@@ -686,6 +1486,44 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
686
1486
|
/**
|
|
687
1487
|
* Searches for entries within a key range.
|
|
688
1488
|
* @remarks Time O(log n + k), Space O(k) where k is result size
|
|
1489
|
+
|
|
1490
|
+
|
|
1491
|
+
|
|
1492
|
+
|
|
1493
|
+
|
|
1494
|
+
|
|
1495
|
+
|
|
1496
|
+
|
|
1497
|
+
|
|
1498
|
+
|
|
1499
|
+
|
|
1500
|
+
|
|
1501
|
+
|
|
1502
|
+
|
|
1503
|
+
|
|
1504
|
+
|
|
1505
|
+
|
|
1506
|
+
|
|
1507
|
+
|
|
1508
|
+
|
|
1509
|
+
|
|
1510
|
+
|
|
1511
|
+
|
|
1512
|
+
|
|
1513
|
+
|
|
1514
|
+
|
|
1515
|
+
|
|
1516
|
+
|
|
1517
|
+
|
|
1518
|
+
|
|
1519
|
+
* @example
|
|
1520
|
+
* // Find keys in range
|
|
1521
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
1522
|
+
* mm.add(10, 'a');
|
|
1523
|
+
* mm.add(20, 'b');
|
|
1524
|
+
* mm.add(30, 'c');
|
|
1525
|
+
* const result = mm.rangeSearch([15, 25]);
|
|
1526
|
+
* console.log(result.length); // 1;
|
|
689
1527
|
*/
|
|
690
1528
|
rangeSearch<C extends (node: RedBlackTreeNode<K, V[]>) => unknown>(
|
|
691
1529
|
range: Range<K> | [K, K],
|
|
@@ -697,6 +1535,51 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
|
|
|
697
1535
|
/**
|
|
698
1536
|
* Creates a shallow clone of this map.
|
|
699
1537
|
* @remarks Time O(n log n), Space O(n)
|
|
1538
|
+
|
|
1539
|
+
|
|
1540
|
+
|
|
1541
|
+
|
|
1542
|
+
|
|
1543
|
+
|
|
1544
|
+
|
|
1545
|
+
|
|
1546
|
+
|
|
1547
|
+
|
|
1548
|
+
|
|
1549
|
+
|
|
1550
|
+
|
|
1551
|
+
|
|
1552
|
+
|
|
1553
|
+
|
|
1554
|
+
|
|
1555
|
+
|
|
1556
|
+
|
|
1557
|
+
|
|
1558
|
+
|
|
1559
|
+
|
|
1560
|
+
|
|
1561
|
+
|
|
1562
|
+
|
|
1563
|
+
|
|
1564
|
+
|
|
1565
|
+
|
|
1566
|
+
|
|
1567
|
+
|
|
1568
|
+
|
|
1569
|
+
|
|
1570
|
+
|
|
1571
|
+
|
|
1572
|
+
|
|
1573
|
+
|
|
1574
|
+
|
|
1575
|
+
|
|
1576
|
+
* @example
|
|
1577
|
+
* // Deep clone
|
|
1578
|
+
* const mm = new TreeMultiMap<number, string>();
|
|
1579
|
+
* mm.add(1, 'a');
|
|
1580
|
+
* const copy = mm.clone();
|
|
1581
|
+
* copy.delete(1);
|
|
1582
|
+
* console.log(mm.has(1)); // true;
|
|
700
1583
|
*/
|
|
701
1584
|
clone(): TreeMultiMap<K, V, R> {
|
|
702
1585
|
return new TreeMultiMap<K, V, R>(this, { comparator: this.comparator, isMapMode: this.#core.isMapMode });
|