typescript-dsa-stl 2.6.0 → 2.7.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 (2) hide show
  1. package/README.md +432 -373
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,17 +1,58 @@
1
1
  # TypeScript_DSA
2
2
 
3
- **This is the GitHub repository** for the npm package **[typescript-dsa-stl](https://www.npmjs.com/package/typescript-dsa-stl)**.
3
+ **Repository** for the npm package **[typescript-dsa-stl](https://www.npmjs.com/package/typescript-dsa-stl)** · [GitHub](https://github.com/SajidAbdullah729/TypeScript_DSA)
4
4
 
5
- STL-style data structures and algorithms for TypeScript: **Vector**, **Stack**, **Queue**, **Deque**, **List**, **PriorityQueue**, **OrderedMap** (Map), **UnorderedMap**, **OrderedSet** (Set), **UnorderedSet**, **OrderedMultiMap**, **OrderedMultiSet**, **segment trees** (`SegmentTreeSum`, `SegmentTreeMin`, `SegmentTreeMax`, `SegmentTree`, `GeneralSegmentTree`, `LazySegmentTreeSum`), and algorithms (`sort`, `binarySearch`, `lowerBound`, `min`, `max`, **KnuthMorrisPratt**, **RabinKarp**, **StringRollingHash**, etc.). Install from npm to use in your project; this repo holds the source code.
5
+ STL-style data structures and algorithms for TypeScript: **Vector**, **Stack**, **Queue**, **Deque**, **List**, **PriorityQueue**, ordered/unordered **Map** and **Set**, **OrderedMultiMap** / **OrderedMultiSet**, **segment trees**, **graph** helpers (BFS, DFS, Dijkstra, Kruskal, DSU), and **string** algorithms (KMP, Rabin–Karp, rolling hash). Install from npm for your app; this repo is the source.
6
6
 
7
7
  ---
8
8
 
9
- ## Install
9
+ ## Table of contents
10
+
11
+ | Section | What you’ll find |
12
+ |--------|-------------------|
13
+ | [Installation](#installation) | npm install |
14
+ | [Package layout & imports](#package-layout--imports) | Barrel vs subpaths (tree-shaking) |
15
+ | [Quick start](#quick-start) | One file showing main APIs |
16
+ | [API reference](#api-reference) | Export tables |
17
+ | [Complexity](#complexity) | Big-O for collections and segment trees |
18
+ | [Collections](#collections) | Deque, nested vectors, multi-map / multi-set |
19
+ | [Segment trees](#segment-trees) | Sum/min/max, generic, lazy |
20
+ | [Graph algorithms](#graph-algorithms) | Adjacency lists, BFS/DFS, components, MST, shortest paths |
21
+ | [String algorithms](#string-algorithms) | KMP, Rabin–Karp, rolling hash |
22
+ | [For maintainers](#for-maintainers) | Build and publish |
23
+ | [License](#license) | MIT |
24
+
25
+ ---
26
+
27
+ ## Installation
10
28
 
11
29
  ```bash
12
30
  npm install typescript-dsa-stl
13
31
  ```
14
32
 
33
+ **Runtime:** Node **18+** (see `package.json` `engines`).
34
+
35
+ ---
36
+
37
+ ## Package layout & imports
38
+
39
+ Import everything from the root, or use subpaths for smaller bundles:
40
+
41
+ ```ts
42
+ import { Vector, Stack, Queue, Deque } from 'typescript-dsa-stl/collections';
43
+ import {
44
+ sort,
45
+ binarySearch,
46
+ breadthFirstSearch,
47
+ depthFirstSearch,
48
+ KnuthMorrisPratt,
49
+ RabinKarp,
50
+ StringRollingHash,
51
+ } from 'typescript-dsa-stl/algorithms';
52
+ import { clamp, range } from 'typescript-dsa-stl/utils';
53
+ import type { Comparator } from 'typescript-dsa-stl/types';
54
+ ```
55
+
15
56
  ---
16
57
 
17
58
  ## Quick start
@@ -33,6 +74,8 @@ import {
33
74
  sort,
34
75
  find,
35
76
  binarySearch,
77
+ lowerBound,
78
+ upperBound,
36
79
  min,
37
80
  max,
38
81
  clamp,
@@ -112,6 +155,63 @@ clamp(42, 0, 10); // 10
112
155
  range(0, 5); // [0, 1, 2, 3, 4]
113
156
  ```
114
157
 
158
+ ---
159
+
160
+ ## API reference
161
+
162
+ ### Main export map
163
+
164
+ | Area | Exports |
165
+ |------|---------|
166
+ | **Collections** | `Vector`, `Stack`, `Queue`, `Deque`, `List`, `ListNode`, `PriorityQueue`, `OrderedMap`, `UnorderedMap`, `OrderedSet`, `UnorderedSet`, `OrderedMultiMap`, `OrderedMultiSet`, `GeneralSegmentTree`, `SegmentTree`, `SegmentTreeSum`, `SegmentTreeMin`, `SegmentTreeMax`, `LazySegmentTreeSum`, `WeightedEdge`, `AdjacencyList`, `WeightedAdjacencyList`, `createAdjacencyList`, `createWeightedAdjacencyList`, `addEdge`, `deleteEdge` |
167
+ | **Algorithms** | `sort`, `find`, `findIndex`, `transform`, `filter`, `reduce`, `reverse`, `unique`, `binarySearch`, `lowerBound`, `upperBound`, `min`, `max`, `partition`, `DisjointSetUnion`, `KnuthMorrisPratt`, `RabinKarp`, `RABIN_KARP_DEFAULT_MODS`, `StringRollingHash`, `breadthFirstSearch`, `depthFirstSearch`, `connectedComponents`, `kruskalMST`, `dijkstra`, `reconstructPath` |
168
+ | **Utils** | `clamp`, `range`, `noop`, `identity`, `swap` |
169
+ | **Types** | `Comparator`, `Predicate`, `UnaryFn`, `Reducer`, `IterableLike`, `toArray`, `RabinKarpTripleMods`, `WeightedUndirectedEdge`, `GeneralSegmentTreeConfig`, `SegmentCombine`, `SegmentMerge`, `SegmentLeafBuild` |
170
+
171
+ ### Subpath imports (tree-shaking)
172
+
173
+ ```ts
174
+ import { Vector, Stack, Queue, Deque } from 'typescript-dsa-stl/collections';
175
+ import { sort, binarySearch, breadthFirstSearch, depthFirstSearch, KnuthMorrisPratt, RabinKarp, StringRollingHash } from 'typescript-dsa-stl/algorithms';
176
+ import { clamp, range } from 'typescript-dsa-stl/utils';
177
+ import type { Comparator } from 'typescript-dsa-stl/types';
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Complexity
183
+
184
+ ### Linear and associative structures
185
+
186
+ | Structure | Access | Insert end | Insert middle | Remove end | Remove middle |
187
+ |-----------|--------|------------|---------------|------------|---------------|
188
+ | **Vector** | O(1) | O(1)* | O(n) | O(1) | O(n) |
189
+ | **Stack** | — | O(1) | — | O(1) | — |
190
+ | **Queue** | — | O(1)* | — | O(1)* | — |
191
+ | **Deque** | O(1) | O(1)* (front/back) | — | O(1)* (front/back) | — |
192
+ | **List** | O(n) | O(1) | O(1)** | O(1) | O(1)** |
193
+ | **PriorityQueue** | — | O(log n) | — | O(log n) | — |
194
+ | **OrderedMap** (Map) | O(log n) get | O(log n) set | — | O(log n) delete | — |
195
+ | **UnorderedMap** | O(1)* get/set | O(1)* | — | O(1)* delete | — |
196
+ | **OrderedSet** (Set) | O(log n) has | O(log n) add | — | O(log n) delete | — |
197
+ | **UnorderedSet** | O(1)* has/add | O(1)* | — | O(1)* delete | — |
198
+ | **OrderedMultiMap** | O(log n) get | O(log n) set | — | O(log n) delete | — |
199
+ | **OrderedMultiSet** | O(log n) has/count | O(log n) add | — | O(log n) delete | — |
200
+
201
+ \* Amortized (hash).
202
+ \** At a known node.
203
+
204
+ ### Segment trees
205
+
206
+ | Structure | Build | Point update | Range query | Extra |
207
+ |-----------|-------|--------------|-------------|--------|
208
+ | **GeneralSegmentTree**, **SegmentTree**, **SegmentTreeSum** / **Min** / **Max** | O(n) | O(log n) | O(log n) | Inclusive `[l, r]`; **GeneralSegmentTree** keeps raw `V` and uses `merge` + `buildLeaf` |
209
+ | **LazySegmentTreeSum** | O(n) | `set`: O(log n) | `rangeSum`: O(log n) | `rangeAdd` on a range: O(log n) |
210
+
211
+ ---
212
+
213
+ ## Collections
214
+
115
215
  ### Deque (like C++ `std::deque`)
116
216
 
117
217
  A **double-ended queue**: amortized **O(1)** `pushFront` / `pushBack` / `popFront` / `popBack`, and **O(1)** random access via `at` / `set`. Implemented as a growable circular buffer (same asymptotics as a typical `std::deque` for these operations).
@@ -178,136 +278,348 @@ cube.push(layer0);
178
278
  cube.at(0).at(1).at(0); // 3 (layer 0, row 1, col 0)
179
279
  ```
180
280
 
181
- ### Graph adjacency list (like C++ `vector<vector<type>> graph(n)`)
281
+ ### OrderedMultiMap and OrderedMultiSet use cases
182
282
 
183
- You can model C++-style adjacency lists using the graph types and helpers exported from `typescript-dsa-stl/collections` (or the main package).
283
+ **OrderedMultiSet** is a sorted collection that allows duplicate elements (like C++ `std::multiset`). Use it when you need ordering and multiple copies of the same value.
184
284
 
185
- #### Unweighted adjacency list
285
+ | Use case | Example |
286
+ |----------|---------|
287
+ | **Sorted runs / leaderboard with ties** | Store scores; multiple users can have the same score. Iterate in sorted order, use `count(score)` for ties. |
288
+ | **Event timeline with repeated timestamps** | Add events by time; several events can share the same time. `add(timestamp)`, iterate in order. |
289
+ | **K-th smallest in a multiset** | Keep elements sorted; k-th element is at index `k - 1` in iteration. |
290
+ | **Range counts** | Combined with binary search ideas: count elements in `[low, high]` using `count` and iteration. |
186
291
 
187
- C++:
292
+ **OrderedMultiMap** maps one key to multiple values while keeping keys sorted (like C++ `std::multimap`). Use it when a key can have several associated values and you need key order.
188
293
 
189
- ```cpp
190
- int n = 5;
191
- vector<vector<int>> graph(n);
192
- graph[u].push_back(v); // or graph[u].pb(v);
193
- ```
294
+ | Use case | Example |
295
+ |----------|---------|
296
+ | **Inverted index** | Key = term, values = document IDs containing that term. `set(term, docId)` for each occurrence; `getAll(term)` returns all doc IDs. |
297
+ | **Grouping by key** | Key = category, values = items. `set(category, item)`; iterate keys in order, use `getAll(key)` per group. |
298
+ | **One-to-many relations** | Key = user ID, values = session IDs. `set(userId, sessionId)`; `getAll(userId)` lists all sessions. |
299
+ | **Time-series by bucket** | Key = time bucket, values = events. Sorted keys give chronological buckets; `getAll(bucket)` gets events in that bucket. |
194
300
 
195
- TypeScript (easy declaration with `createAdjacencyList`):
301
+ **OrderedMultiSet example:**
196
302
 
197
303
  ```ts
198
- import { createAdjacencyList } from 'typescript-dsa-stl/collections';
199
-
200
- const n = 5;
201
- const graph = createAdjacencyList(n); // empty graph with n vertices
202
-
203
- // C++: graph[u].push_back(v);
204
- graph[u].push(v);
304
+ import { OrderedMultiSet } from 'typescript-dsa-stl';
205
305
 
206
- // Iteration is the same idea as in C++
207
- for (const v of graph[u]) {
208
- // neighbor v
209
- }
306
+ const scores = new OrderedMultiSet<number>();
307
+ scores.add(85); scores.add(92); scores.add(85); scores.add(78);
308
+ console.log(scores.toArray()); // [78, 85, 85, 92]
309
+ console.log(scores.count(85)); // 2
310
+ scores.delete(85); // remove one 85
311
+ console.log(scores.count(85)); // 1
312
+ scores.deleteAll(85); // remove all 85s
210
313
  ```
211
314
 
212
- Or with helpers `addEdge` / `deleteEdge`:
315
+ **OrderedMultiMap example:**
213
316
 
214
317
  ```ts
215
- import { createAdjacencyList, addEdge, deleteEdge } from 'typescript-dsa-stl/collections';
216
-
217
- const graph = createAdjacencyList(5);
318
+ import { OrderedMultiMap } from 'typescript-dsa-stl';
218
319
 
219
- addEdge(graph, u, v); // add u -> v
220
- deleteEdge(graph, u, v); // remove all edges u -> v
320
+ const index = new OrderedMultiMap<string, number>(); // term -> doc IDs
321
+ index.set('typescript', 1); index.set('typescript', 3); index.set('stl', 2);
322
+ console.log(index.getAll('typescript')); // [1, 3]
323
+ console.log(index.get('stl')); // 2
324
+ for (const [key, value] of index) {
325
+ console.log(key, value); // keys in sorted order
326
+ }
221
327
  ```
222
328
 
223
- #### Weighted adjacency list
329
+ ---
224
330
 
225
- In C++ you might write:
331
+ ## Segment trees
226
332
 
227
- ```cpp
228
- int n = 5;
229
- vector<vector<pair<int,int>>> graph(n);
230
- graph[u].push_back({v, w}); // edge u -> v with weight w
231
- ```
333
+ Segment trees support **range queries** and **point updates** in **O(log n)**. Range endpoints are **inclusive**: `query(l, r)` covers indices `l` through `r`.
232
334
 
233
- In TypeScript, use `createWeightedAdjacencyList` for easy declaration:
335
+ ### Ready-made variants (`SegmentTreeSum`, `SegmentTreeMin`, `SegmentTreeMax`)
234
336
 
235
337
  ```ts
236
- import { createWeightedAdjacencyList } from 'typescript-dsa-stl/collections';
338
+ import {
339
+ SegmentTreeSum,
340
+ SegmentTreeMin,
341
+ SegmentTreeMax,
342
+ } from 'typescript-dsa-stl';
237
343
 
238
- const n = 5;
239
- const graph = createWeightedAdjacencyList(n); // empty weighted graph with n vertices
344
+ const sum = new SegmentTreeSum([1, 2, 3, 4]);
345
+ console.log(sum.query(0, 3)); // 10
346
+ sum.update(1, 10);
347
+ console.log(sum.query(0, 3)); // 1 + 10 + 3 + 4 = 18
240
348
 
241
- // C++: graph[u].push_back({v, w});
242
- graph[u].push({ to: v, weight: w });
349
+ const mn = new SegmentTreeMin([5, 2, 8, 1]);
350
+ console.log(mn.query(0, 3)); // 1
243
351
 
244
- // When iterating, you get both neighbor and weight
245
- for (const { to, weight } of graph[u]) {
246
- // edge u -> to with cost = weight
247
- }
352
+ const mx = new SegmentTreeMax([5, 2, 8, 1]);
353
+ console.log(mx.query(0, 3)); // 8
248
354
  ```
249
355
 
250
- Or with the helper functions `addEdge` / `deleteEdge`:
356
+ ### Generic `SegmentTree<T>` (custom combine + neutral)
357
+
358
+ Use the same type for array elements and aggregates. Pass an **associative** `combine` and a **neutral** value for query ranges that miss a segment (e.g. `0` for sum, `Infinity` for min).
251
359
 
252
360
  ```ts
253
- import { createWeightedAdjacencyList, addEdge, deleteEdge } from 'typescript-dsa-stl/collections';
361
+ import { SegmentTree } from 'typescript-dsa-stl';
254
362
 
255
- const graph = createWeightedAdjacencyList(5);
363
+ const gcdTree = new SegmentTree<number>(
364
+ [12, 18, 24],
365
+ (a, b) => {
366
+ let x = a;
367
+ let y = b;
368
+ while (y !== 0) {
369
+ const t = y;
370
+ y = x % y;
371
+ x = t;
372
+ }
373
+ return x;
374
+ },
375
+ 0
376
+ );
377
+ console.log(gcdTree.query(0, 2)); // gcd(12, 18, 24) === 6
256
378
 
257
- addEdge(graph, u, v, w); // add u -> v with weight w
258
- deleteEdge(graph, u, v, w); // delete all edges u -> v with weight w
379
+ // Non-numeric example: concatenate strings
380
+ const strTree = new SegmentTree<string>(
381
+ ['a', 'b', 'c'],
382
+ (a, b) => a + b,
383
+ ''
384
+ );
385
+ console.log(strTree.query(0, 2)); // 'abc'
259
386
  ```
260
387
 
261
- #### Graph adjacency list use cases
262
-
263
- Use an **unweighted** graph (adjacency list) when you only care about connectivity; use a **weighted** graph when edges have costs (distance, time, capacity).
264
-
265
- | Use case | When to use |
266
- |----------|-------------|
267
- | **BFS / DFS, connectivity** | Unweighted: shortest path in terms of hop count, connected components, cycle detection. |
268
- | **Shortest path (Dijkstra), MST** | Weighted: edge weights as distances or costs; run Dijkstra, Prim, or Kruskal on the list. |
269
- | **Social / dependency graphs** | Unweighted or weighted: followers, dependencies (e.g. build order), recommendation graphs. |
270
- | **Grid / game graphs** | Unweighted: 4- or 8-neighbor grids; weighted if movement costs differ per cell. |
271
- | **Network / flow** | Weighted: capacities or latencies on edges for max-flow or routing. |
388
+ ### `GeneralSegmentTree<T, V>` (custom merge + buildLeaf)
272
389
 
273
- #### Breadth-first search (BFS) and depth-first search (DFS)
390
+ Use when **raw** values `V` differ from the **aggregate** type `T`:
274
391
 
275
- `breadthFirstSearch` and `depthFirstSearch` take the number of vertices `n`, an unweighted `AdjacencyList`, and a `start` vertex. They return the **visit order** for all vertices **reachable** from `start` (vertices outside that component are not included). For an undirected graph, add each edge in **both** directions (see `addEdge` below).
392
+ - **`merge(left, right)`** combine two child aggregates (internal nodes).
393
+ - **`neutral`** — identity for `merge` when a query does not overlap a segment.
394
+ - **`buildLeaf(value, index)`** — build the leaf from the raw array on initial construction and on every `update`.
276
395
 
277
- **Example graph (diamond):** edges `0—1`, `0—2`, `1—3`, `2—3`.
396
+ ```ts
397
+ import { GeneralSegmentTree } from 'typescript-dsa-stl';
278
398
 
279
- ```text
280
- 0
281
- / \
282
- 1 2
283
- \ /
284
- 3
399
+ // Store sum of squares; raw array is plain numbers
400
+ const st = new GeneralSegmentTree<number, number>([1, 2, 3], {
401
+ merge: (a, b) => a + b,
402
+ neutral: 0,
403
+ buildLeaf: (v, i) => v * v + i,
404
+ });
405
+ console.log(st.query(0, 2)); // (1+0) + (4+1) + (9+2) = 17
406
+ st.update(1, 4);
407
+ console.log(st.rawAt(1)); // 4 — current raw value at index 1
285
408
  ```
286
409
 
287
- With neighbors listed in ascending vertex id (`0: [1,2]`, `1: [0,3]`, …), **BFS** from `0` visits by increasing distance from `0`: first `0`, then `1` and `2`, then `3` → order `[0, 1, 2, 3]`. **DFS** (preorder, first neighbor in each list first) goes `0 → 1 → 3` then `2` → order `[0, 1, 3, 2]`. The exact DFS order depends on how you order each adjacency list.
410
+ ### `LazySegmentTreeSum` (range add + range sum)
411
+
412
+ **`rangeAdd(l, r, delta)`** adds `delta` to every element in the inclusive range. **`rangeSum(l, r)`** returns the sum. **`set(i, value)`** assigns one position (lazy tags are applied along the path). All are **O(log n)**.
288
413
 
289
414
  ```ts
290
- import {
291
- createAdjacencyList,
292
- addEdge,
293
- breadthFirstSearch,
294
- depthFirstSearch,
295
- } from 'typescript-dsa-stl';
415
+ import { LazySegmentTreeSum } from 'typescript-dsa-stl';
296
416
 
297
- const n = 4;
298
- const graph = createAdjacencyList(n);
417
+ const lazy = new LazySegmentTreeSum([0, 0, 0, 0]);
418
+ lazy.rangeAdd(1, 2, 5); // indices 1 and 2 get +5
419
+ console.log(lazy.rangeSum(0, 3)); // 10
420
+ lazy.set(0, 100);
421
+ console.log(lazy.rangeSum(0, 3)); // 100 + 5 + 5 + 0
422
+ ```
299
423
 
300
- // Undirected diamond: add both directions for each edge
301
- addEdge(graph, 0, 1);
302
- addEdge(graph, 1, 0);
303
- addEdge(graph, 0, 2);
304
- addEdge(graph, 2, 0);
305
- addEdge(graph, 1, 3);
306
- addEdge(graph, 3, 1);
307
- addEdge(graph, 2, 3);
308
- addEdge(graph, 3, 2);
424
+ ### Real-world use cases
309
425
 
310
- const start = 0;
426
+ These patterns show up in backends and internal tools when you need **many** range queries and updates on a fixed sequence (length known up front), without scanning the whole array each time.
427
+
428
+ #### 1. Analytics or reporting: totals over a time window (with corrections)
429
+
430
+ Each index is a **fixed bucket** (hour of day, day of month, version slot, etc.). You repeatedly ask “what is the **sum** from bucket `a` through `b`?” and sometimes **fix one bucket** after late data or a reconciliation.
431
+
432
+ ```ts
433
+ import { SegmentTreeSum } from 'typescript-dsa-stl';
434
+
435
+ /** Revenue (or events, page views, API calls) per calendar day; index 0 = first day of period. */
436
+ class PeriodMetrics {
437
+ private readonly tree: SegmentTreeSum;
438
+
439
+ constructor(dailyValues: readonly number[]) {
440
+ this.tree = new SegmentTreeSum(dailyValues);
441
+ }
442
+
443
+ /** Total for an inclusive day range — e.g. chart drill-down or export row. */
444
+ totalBetweenDay(firstDayIndex: number, lastDayIndex: number): number {
445
+ return this.tree.query(firstDayIndex, lastDayIndex);
446
+ }
447
+
448
+ /** Backfill or correct one day without rebuilding the whole series. */
449
+ setDay(dayIndex: number, amount: number): void {
450
+ this.tree.update(dayIndex, amount);
451
+ }
452
+ }
453
+
454
+ const january = new PeriodMetrics([1200, 980, 1100, 1050, 1300]);
455
+ console.log(january.totalBetweenDay(0, 4)); // full period
456
+ january.setDay(2, 1150); // corrected day 2
457
+ console.log(january.totalBetweenDay(1, 3)); // sum over days 1..3
458
+ ```
459
+
460
+ In production you would usually **persist** the underlying series in a database and **rebuild** the tree when the period reloads; the tree stays useful in memory for dashboards, simulations, or request handlers that see heavy read/update traffic on the same window.
461
+
462
+ #### 2. Operations or finance: bulk adjustment on a slice, then aggregate
463
+
464
+ You apply the **same delta** to **every** element in an index range (tiered bonuses, prorated credits, simulation shocks), then need **range sums** for reporting. A lazy sum tree avoids touching each cell one by one.
465
+
466
+ ```ts
467
+ import { LazySegmentTreeSum } from 'typescript-dsa-stl';
468
+
469
+ /** Example: per-seat or per-row amounts; apply a flat bonus to ranks 10–50 (0-based 9..49), then sum a sub-range for a sub-team. */
470
+ function simulateBulkBonusAndSubtotal(seatCount: number): void {
471
+ // Initial per-seat values (e.g. base commission), built once
472
+ const base = Array.from({ length: seatCount }, (_, i) => 100 + i);
473
+ const amounts = new LazySegmentTreeSum(base);
474
+
475
+ // HR: +250 to everyone in seats 10–50 inclusive (indices 9..49)
476
+ amounts.rangeAdd(9, 49, 250);
477
+
478
+ // Finance: subtotal for seats 20–30 only
479
+ console.log(amounts.rangeSum(19, 29));
480
+ }
481
+
482
+ simulateBulkBonusAndSubtotal(100);
483
+ ```
484
+
485
+ The same idea applies to **inventory deltas** across bin ranges, **loyalty points** batch credits by user-ID band (when IDs map to contiguous indices), or **game/simulation** state where many cells gain the same buff and you query partial totals.
486
+
487
+ ---
488
+
489
+ ## Graph algorithms
490
+
491
+ Graph helpers live on the main package and under `typescript-dsa-stl/collections` for adjacency types and factories.
492
+
493
+ ### Adjacency list (like C++ `vector<vector<type>> graph(n)`)
494
+
495
+ You can model C++-style adjacency lists using the graph types and helpers exported from `typescript-dsa-stl/collections` (or the main package).
496
+
497
+ #### Unweighted adjacency list
498
+
499
+ C++:
500
+
501
+ ```cpp
502
+ int n = 5;
503
+ vector<vector<int>> graph(n);
504
+ graph[u].push_back(v); // or graph[u].pb(v);
505
+ ```
506
+
507
+ TypeScript (easy declaration with `createAdjacencyList`):
508
+
509
+ ```ts
510
+ import { createAdjacencyList } from 'typescript-dsa-stl/collections';
511
+
512
+ const n = 5;
513
+ const graph = createAdjacencyList(n); // empty graph with n vertices
514
+
515
+ // C++: graph[u].push_back(v);
516
+ graph[u].push(v);
517
+
518
+ // Iteration is the same idea as in C++
519
+ for (const v of graph[u]) {
520
+ // neighbor v
521
+ }
522
+ ```
523
+
524
+ Or with helpers `addEdge` / `deleteEdge`:
525
+
526
+ ```ts
527
+ import { createAdjacencyList, addEdge, deleteEdge } from 'typescript-dsa-stl/collections';
528
+
529
+ const graph = createAdjacencyList(5);
530
+
531
+ addEdge(graph, u, v); // add u -> v
532
+ deleteEdge(graph, u, v); // remove all edges u -> v
533
+ ```
534
+
535
+ #### Weighted adjacency list
536
+
537
+ In C++ you might write:
538
+
539
+ ```cpp
540
+ int n = 5;
541
+ vector<vector<pair<int,int>>> graph(n);
542
+ graph[u].push_back({v, w}); // edge u -> v with weight w
543
+ ```
544
+
545
+ In TypeScript, use `createWeightedAdjacencyList` for easy declaration:
546
+
547
+ ```ts
548
+ import { createWeightedAdjacencyList } from 'typescript-dsa-stl/collections';
549
+
550
+ const n = 5;
551
+ const graph = createWeightedAdjacencyList(n); // empty weighted graph with n vertices
552
+
553
+ // C++: graph[u].push_back({v, w});
554
+ graph[u].push({ to: v, weight: w });
555
+
556
+ // When iterating, you get both neighbor and weight
557
+ for (const { to, weight } of graph[u]) {
558
+ // edge u -> to with cost = weight
559
+ }
560
+ ```
561
+
562
+ Or with the helper functions `addEdge` / `deleteEdge`:
563
+
564
+ ```ts
565
+ import { createWeightedAdjacencyList, addEdge, deleteEdge } from 'typescript-dsa-stl/collections';
566
+
567
+ const graph = createWeightedAdjacencyList(5);
568
+
569
+ addEdge(graph, u, v, w); // add u -> v with weight w
570
+ deleteEdge(graph, u, v, w); // delete all edges u -> v with weight w
571
+ ```
572
+
573
+ #### Graph adjacency list — use cases
574
+
575
+ Use an **unweighted** graph (adjacency list) when you only care about connectivity; use a **weighted** graph when edges have costs (distance, time, capacity).
576
+
577
+ | Use case | When to use |
578
+ |----------|-------------|
579
+ | **BFS / DFS, connectivity** | Unweighted: shortest path in terms of hop count, connected components, cycle detection. |
580
+ | **Shortest path (Dijkstra), MST** | Weighted: edge weights as distances or costs; run Dijkstra, Prim, or Kruskal on the list. |
581
+ | **Social / dependency graphs** | Unweighted or weighted: followers, dependencies (e.g. build order), recommendation graphs. |
582
+ | **Grid / game graphs** | Unweighted: 4- or 8-neighbor grids; weighted if movement costs differ per cell. |
583
+ | **Network / flow** | Weighted: capacities or latencies on edges for max-flow or routing. |
584
+
585
+ ### Breadth-first search (BFS) and depth-first search (DFS)
586
+
587
+ `breadthFirstSearch` and `depthFirstSearch` take the number of vertices `n`, an unweighted `AdjacencyList`, and a `start` vertex. They return the **visit order** for all vertices **reachable** from `start` (vertices outside that component are not included). For an undirected graph, add each edge in **both** directions (see `addEdge` below).
588
+
589
+ **Example graph (diamond):** edges `0—1`, `0—2`, `1—3`, `2—3`.
590
+
591
+ ```text
592
+ 0
593
+ / \
594
+ 1 2
595
+ \ /
596
+ 3
597
+ ```
598
+
599
+ With neighbors listed in ascending vertex id (`0: [1,2]`, `1: [0,3]`, …), **BFS** from `0` visits by increasing distance from `0`: first `0`, then `1` and `2`, then `3` → order `[0, 1, 2, 3]`. **DFS** (preorder, first neighbor in each list first) goes `0 → 1 → 3` then `2` → order `[0, 1, 3, 2]`. The exact DFS order depends on how you order each adjacency list.
600
+
601
+ ```ts
602
+ import {
603
+ createAdjacencyList,
604
+ addEdge,
605
+ breadthFirstSearch,
606
+ depthFirstSearch,
607
+ } from 'typescript-dsa-stl';
608
+
609
+ const n = 4;
610
+ const graph = createAdjacencyList(n);
611
+
612
+ // Undirected diamond: add both directions for each edge
613
+ addEdge(graph, 0, 1);
614
+ addEdge(graph, 1, 0);
615
+ addEdge(graph, 0, 2);
616
+ addEdge(graph, 2, 0);
617
+ addEdge(graph, 1, 3);
618
+ addEdge(graph, 3, 1);
619
+ addEdge(graph, 2, 3);
620
+ addEdge(graph, 3, 2);
621
+
622
+ const start = 0;
311
623
 
312
624
  // BFS: level-by-level from start (hop count); output: [0, 1, 2, 3]
313
625
  console.log(breadthFirstSearch(n, graph, start));
@@ -334,7 +646,7 @@ console.log(breadthFirstSearch(5, withIsolated, 0)); // [0, 1] — not [0,1,2,3,
334
646
  - **Disconnected graphs:** run again from another unvisited `start`, or use `connectedComponents` to enumerate components first.
335
647
  - **Weighted graphs:** for traversal ignoring weights, use the same vertex lists as the unweighted graph (weights are ignored by these two functions).
336
648
 
337
- #### Disjoint Set Union (Union-Find)
649
+ ### Disjoint Set Union (Union-Find)
338
650
 
339
651
  Use Union-Find (DSU) to compute connected components efficiently. It merges endpoints of every edge in the adjacency list, so for directed graphs it returns weak connectivity components.
340
652
 
@@ -352,7 +664,7 @@ const comps = connectedComponents(n, graph);
352
664
  // e.g. [[0, 1], [2], [3, 4]]
353
665
  ```
354
666
 
355
- ##### Traverse the result
667
+ #### Traverse the result
356
668
 
357
669
  `connectedComponents(n, adj)` returns `number[][]` where each inner array is a component (list of vertices).
358
670
 
@@ -369,7 +681,7 @@ for (const comp of comps) {
369
681
  const sizes = comps.map(comp => comp.length);
370
682
  ```
371
683
 
372
- #### Kruskal MST (uses DSU)
684
+ ### Kruskal MST (uses DSU)
373
685
 
374
686
  For a weighted graph, `kruskalMST` builds a Minimum Spanning Tree (MST) using DSU.
375
687
 
@@ -393,7 +705,28 @@ const { edges, totalWeight } = kruskalMST(n, wGraph, { undirected: true });
393
705
  // edges: MST edges (chosen by weight), totalWeight: sum of weights
394
706
  ```
395
707
 
396
- #### Dijkstra shortest paths
708
+ #### Traverse the MST
709
+
710
+ `kruskalMST(...)` returns `{ edges, totalWeight }`. To traverse the MST like a graph, convert `edges` into an adjacency list:
711
+
712
+ ```ts
713
+ import { createWeightedAdjacencyList } from 'typescript-dsa-stl/collections';
714
+
715
+ const mstAdj = createWeightedAdjacencyList(n);
716
+
717
+ for (const { u, v, weight } of edges) {
718
+ // MST is undirected (we used { undirected: true })
719
+ mstAdj[u].push({ to: v, weight });
720
+ mstAdj[v].push({ to: u, weight });
721
+ }
722
+
723
+ // Example: iterate neighbors of vertex 0 in the MST
724
+ for (const { to, weight } of mstAdj[0]) {
725
+ // visit edge 0 -> to (weight)
726
+ }
727
+ ```
728
+
729
+ ### Dijkstra shortest paths
397
730
 
398
731
  `dijkstra` computes single-source shortest paths on a **weighted** graph with **non-negative** edge weights.
399
732
  It returns:
@@ -425,32 +758,15 @@ const path = reconstructPath(prev, 0, target); // [0, ..., target] or [] if unre
425
758
  console.log(path); // [0, 1, 2, 4]
426
759
  ```
427
760
 
428
- ##### Traverse the MST
429
-
430
- `kruskalMST(...)` returns `{ edges, totalWeight }`. To traverse the MST like a graph, convert `edges` into an adjacency list:
431
-
432
- ```ts
433
- import { createWeightedAdjacencyList } from 'typescript-dsa-stl/collections';
434
-
435
- const mstAdj = createWeightedAdjacencyList(n);
436
-
437
- for (const { u, v, weight } of edges) {
438
- // MST is undirected (we used { undirected: true })
439
- mstAdj[u].push({ to: v, weight });
440
- mstAdj[v].push({ to: u, weight });
441
- }
761
+ ---
442
762
 
443
- // Example: iterate neighbors of vertex 0 in the MST
444
- for (const { to, weight } of mstAdj[0]) {
445
- // visit edge 0 -> to (weight)
446
- }
447
- ```
763
+ ## String algorithms
448
764
 
449
- #### Knuth–Morris–Pratt (KMP), Rabin–Karp, and string rolling hash
765
+ ### Knuth–Morris–Pratt (KMP), Rabin–Karp, and string rolling hash
450
766
 
451
767
  All three work on **UTF-16 code units** (same as `String` indexing). They solve **different jobs**: KMP and Rabin–Karp are **pattern matchers** (list all start indices of a pattern in a text). `StringRollingHash` is a **substring-hash tool** on a **fixed** string—you combine it with your own logic (equality checks, binary search, etc.).
452
768
 
453
- ##### When to use which
769
+ #### When to use which
454
770
 
455
771
  | Goal | Prefer | Why |
456
772
  |------|--------|-----|
@@ -555,263 +871,6 @@ console.log(a.substringHash(2, 2) === b.fullHash()); // true — both are "na"
555
871
 
556
872
  ---
557
873
 
558
- ## Segment trees
559
-
560
- Segment trees support **range queries** and **point updates** in **O(log n)**. Range endpoints are **inclusive**: `query(l, r)` covers indices `l` through `r`.
561
-
562
- ### Ready-made variants (`SegmentTreeSum`, `SegmentTreeMin`, `SegmentTreeMax`)
563
-
564
- ```ts
565
- import {
566
- SegmentTreeSum,
567
- SegmentTreeMin,
568
- SegmentTreeMax,
569
- } from 'typescript-dsa-stl';
570
-
571
- const sum = new SegmentTreeSum([1, 2, 3, 4]);
572
- console.log(sum.query(0, 3)); // 10
573
- sum.update(1, 10);
574
- console.log(sum.query(0, 3)); // 1 + 10 + 3 + 4 = 18
575
-
576
- const mn = new SegmentTreeMin([5, 2, 8, 1]);
577
- console.log(mn.query(0, 3)); // 1
578
-
579
- const mx = new SegmentTreeMax([5, 2, 8, 1]);
580
- console.log(mx.query(0, 3)); // 8
581
- ```
582
-
583
- ### Generic `SegmentTree<T>` (custom combine + neutral)
584
-
585
- Use the same type for array elements and aggregates. Pass an **associative** `combine` and a **neutral** value for query ranges that miss a segment (e.g. `0` for sum, `Infinity` for min).
586
-
587
- ```ts
588
- import { SegmentTree } from 'typescript-dsa-stl';
589
-
590
- const gcdTree = new SegmentTree<number>(
591
- [12, 18, 24],
592
- (a, b) => {
593
- let x = a;
594
- let y = b;
595
- while (y !== 0) {
596
- const t = y;
597
- y = x % y;
598
- x = t;
599
- }
600
- return x;
601
- },
602
- 0
603
- );
604
- console.log(gcdTree.query(0, 2)); // gcd(12, 18, 24) === 6
605
-
606
- // Non-numeric example: concatenate strings
607
- const strTree = new SegmentTree<string>(
608
- ['a', 'b', 'c'],
609
- (a, b) => a + b,
610
- ''
611
- );
612
- console.log(strTree.query(0, 2)); // 'abc'
613
- ```
614
-
615
- ### `GeneralSegmentTree<T, V>` (custom merge + buildLeaf)
616
-
617
- Use when **raw** values `V` differ from the **aggregate** type `T`:
618
-
619
- - **`merge(left, right)`** — combine two child aggregates (internal nodes).
620
- - **`neutral`** — identity for `merge` when a query does not overlap a segment.
621
- - **`buildLeaf(value, index)`** — build the leaf from the raw array on initial construction and on every `update`.
622
-
623
- ```ts
624
- import { GeneralSegmentTree } from 'typescript-dsa-stl';
625
-
626
- // Store sum of squares; raw array is plain numbers
627
- const st = new GeneralSegmentTree<number, number>([1, 2, 3], {
628
- merge: (a, b) => a + b,
629
- neutral: 0,
630
- buildLeaf: (v, i) => v * v + i,
631
- });
632
- console.log(st.query(0, 2)); // (1+0) + (4+1) + (9+2) = 17
633
- st.update(1, 4);
634
- console.log(st.rawAt(1)); // 4 — current raw value at index 1
635
- ```
636
-
637
- ### `LazySegmentTreeSum` (range add + range sum)
638
-
639
- **`rangeAdd(l, r, delta)`** adds `delta` to every element in the inclusive range. **`rangeSum(l, r)`** returns the sum. **`set(i, value)`** assigns one position (lazy tags are applied along the path). All are **O(log n)**.
640
-
641
- ```ts
642
- import { LazySegmentTreeSum } from 'typescript-dsa-stl';
643
-
644
- const lazy = new LazySegmentTreeSum([0, 0, 0, 0]);
645
- lazy.rangeAdd(1, 2, 5); // indices 1 and 2 get +5
646
- console.log(lazy.rangeSum(0, 3)); // 10
647
- lazy.set(0, 100);
648
- console.log(lazy.rangeSum(0, 3)); // 100 + 5 + 5 + 0
649
- ```
650
-
651
- ### Real-world use cases
652
-
653
- These patterns show up in backends and internal tools when you need **many** range queries and updates on a fixed sequence (length known up front), without scanning the whole array each time.
654
-
655
- #### 1. Analytics or reporting: totals over a time window (with corrections)
656
-
657
- Each index is a **fixed bucket** (hour of day, day of month, version slot, etc.). You repeatedly ask “what is the **sum** from bucket `a` through `b`?” and sometimes **fix one bucket** after late data or a reconciliation.
658
-
659
- ```ts
660
- import { SegmentTreeSum } from 'typescript-dsa-stl';
661
-
662
- /** Revenue (or events, page views, API calls) per calendar day; index 0 = first day of period. */
663
- class PeriodMetrics {
664
- private readonly tree: SegmentTreeSum;
665
-
666
- constructor(dailyValues: readonly number[]) {
667
- this.tree = new SegmentTreeSum(dailyValues);
668
- }
669
-
670
- /** Total for an inclusive day range — e.g. chart drill-down or export row. */
671
- totalBetweenDay(firstDayIndex: number, lastDayIndex: number): number {
672
- return this.tree.query(firstDayIndex, lastDayIndex);
673
- }
674
-
675
- /** Backfill or correct one day without rebuilding the whole series. */
676
- setDay(dayIndex: number, amount: number): void {
677
- this.tree.update(dayIndex, amount);
678
- }
679
- }
680
-
681
- const january = new PeriodMetrics([1200, 980, 1100, 1050, 1300]);
682
- console.log(january.totalBetweenDay(0, 4)); // full period
683
- january.setDay(2, 1150); // corrected day 2
684
- console.log(january.totalBetweenDay(1, 3)); // sum over days 1..3
685
- ```
686
-
687
- In production you would usually **persist** the underlying series in a database and **rebuild** the tree when the period reloads; the tree stays useful in memory for dashboards, simulations, or request handlers that see heavy read/update traffic on the same window.
688
-
689
- #### 2. Operations or finance: bulk adjustment on a slice, then aggregate
690
-
691
- You apply the **same delta** to **every** element in an index range (tiered bonuses, prorated credits, simulation shocks), then need **range sums** for reporting. A lazy sum tree avoids touching each cell one by one.
692
-
693
- ```ts
694
- import { LazySegmentTreeSum } from 'typescript-dsa-stl';
695
-
696
- /** Example: per-seat or per-row amounts; apply a flat bonus to ranks 10–50 (0-based 9..49), then sum a sub-range for a sub-team. */
697
- function simulateBulkBonusAndSubtotal(seatCount: number): void {
698
- // Initial per-seat values (e.g. base commission), built once
699
- const base = Array.from({ length: seatCount }, (_, i) => 100 + i);
700
- const amounts = new LazySegmentTreeSum(base);
701
-
702
- // HR: +250 to everyone in seats 10–50 inclusive (indices 9..49)
703
- amounts.rangeAdd(9, 49, 250);
704
-
705
- // Finance: subtotal for seats 20–30 only
706
- console.log(amounts.rangeSum(19, 29));
707
- }
708
-
709
- simulateBulkBonusAndSubtotal(100);
710
- ```
711
-
712
- The same idea applies to **inventory deltas** across bin ranges, **loyalty points** batch credits by user-ID band (when IDs map to contiguous indices), or **game/simulation** state where many cells gain the same buff and you query partial totals.
713
-
714
- ---
715
-
716
- ## API overview
717
-
718
- | Module | Exports |
719
- |--------|--------|
720
- | **Collections** | `Vector`, `Stack`, `Queue`, `Deque`, `List`, `ListNode`, `PriorityQueue`, `OrderedMap`, `UnorderedMap`, `OrderedSet`, `UnorderedSet`, `OrderedMultiMap`, `OrderedMultiSet`, `GeneralSegmentTree`, `SegmentTree`, `SegmentTreeSum`, `SegmentTreeMin`, `SegmentTreeMax`, `LazySegmentTreeSum`, `WeightedEdge`, `AdjacencyList`, `WeightedAdjacencyList`, `createAdjacencyList`, `createWeightedAdjacencyList`, `addEdge`, `deleteEdge` |
721
- | **Algorithms** | `sort`, `find`, `findIndex`, `transform`, `filter`, `reduce`, `reverse`, `unique`, `binarySearch`, `lowerBound`, `upperBound`, `min`, `max`, `partition`, `DisjointSetUnion`, `KnuthMorrisPratt`, `RabinKarp`, `RABIN_KARP_DEFAULT_MODS`, `StringRollingHash`, `breadthFirstSearch`, `depthFirstSearch`, `connectedComponents`, `kruskalMST` |
722
- | **Utils** | `clamp`, `range`, `noop`, `identity`, `swap` |
723
- | **Types** | `Comparator`, `Predicate`, `UnaryFn`, `Reducer`, `IterableLike`, `toArray`, `RabinKarpTripleMods`, `GeneralSegmentTreeConfig`, `SegmentCombine`, `SegmentMerge`, `SegmentLeafBuild` |
724
-
725
- ### Subpath imports (tree-shaking)
726
-
727
- ```ts
728
- import { Vector, Stack, Queue, Deque } from 'typescript-dsa-stl/collections';
729
- import { sort, binarySearch, breadthFirstSearch, depthFirstSearch, KnuthMorrisPratt, RabinKarp, StringRollingHash } from 'typescript-dsa-stl/algorithms';
730
- import { clamp, range } from 'typescript-dsa-stl/utils';
731
- import type { Comparator } from 'typescript-dsa-stl/types';
732
- ```
733
-
734
- ---
735
-
736
- ## Data structures
737
-
738
- | Structure | Access | Insert end | Insert middle | Remove end | Remove middle |
739
- |-----------|--------|------------|---------------|------------|---------------|
740
- | **Vector** | O(1) | O(1)* | O(n) | O(1) | O(n) |
741
- | **Stack** | — | O(1) | — | O(1) | — |
742
- | **Queue** | — | O(1)* | — | O(1)* | — |
743
- | **Deque** | O(1) | O(1)* (front/back) | — | O(1)* (front/back) | — |
744
- | **List** | O(n) | O(1) | O(1)** | O(1) | O(1)** |
745
- | **PriorityQueue** | — | O(log n) | — | O(log n) | — |
746
- | **OrderedMap** (Map) | O(log n) get | O(log n) set | — | O(log n) delete | — |
747
- | **UnorderedMap** | O(1)* get/set | O(1)* | — | O(1)* delete | — |
748
- | **OrderedSet** (Set) | O(log n) has | O(log n) add | — | O(log n) delete | — |
749
- | **UnorderedSet** | O(1)* has/add | O(1)* | — | O(1)* delete | — |
750
- | **OrderedMultiMap** | O(log n) get | O(log n) set | — | O(log n) delete | — |
751
- | **OrderedMultiSet** | O(log n) has/count | O(log n) add | — | O(log n) delete | — |
752
-
753
- \* Amortized (hash).
754
- \** At a known node.
755
-
756
- ### Segment trees (range queries)
757
-
758
- | Structure | Build | Point update | Range query | Extra |
759
- |-----------|-------|--------------|-------------|--------|
760
- | **GeneralSegmentTree**, **SegmentTree**, **SegmentTreeSum** / **Min** / **Max** | O(n) | O(log n) | O(log n) | Inclusive `[l, r]`; **GeneralSegmentTree** keeps raw `V` and uses `merge` + `buildLeaf` |
761
- | **LazySegmentTreeSum** | O(n) | `set`: O(log n) | `rangeSum`: O(log n) | `rangeAdd` on a range: O(log n) |
762
-
763
- ---
764
-
765
- ## OrderedMultiMap and OrderedMultiSet — use cases
766
-
767
- **OrderedMultiSet** is a sorted collection that allows duplicate elements (like C++ `std::multiset`). Use it when you need ordering and multiple copies of the same value.
768
-
769
- | Use case | Example |
770
- |----------|---------|
771
- | **Sorted runs / leaderboard with ties** | Store scores; multiple users can have the same score. Iterate in sorted order, use `count(score)` for ties. |
772
- | **Event timeline with repeated timestamps** | Add events by time; several events can share the same time. `add(timestamp)`, iterate in order. |
773
- | **K-th smallest in a multiset** | Keep elements sorted; k-th element is at index `k - 1` in iteration. |
774
- | **Range counts** | Combined with binary search ideas: count elements in `[low, high]` using `count` and iteration. |
775
-
776
- **OrderedMultiMap** maps one key to multiple values while keeping keys sorted (like C++ `std::multimap`). Use it when a key can have several associated values and you need key order.
777
-
778
- | Use case | Example |
779
- |----------|---------|
780
- | **Inverted index** | Key = term, values = document IDs containing that term. `set(term, docId)` for each occurrence; `getAll(term)` returns all doc IDs. |
781
- | **Grouping by key** | Key = category, values = items. `set(category, item)`; iterate keys in order, use `getAll(key)` per group. |
782
- | **One-to-many relations** | Key = user ID, values = session IDs. `set(userId, sessionId)`; `getAll(userId)` lists all sessions. |
783
- | **Time-series by bucket** | Key = time bucket, values = events. Sorted keys give chronological buckets; `getAll(bucket)` gets events in that bucket. |
784
-
785
- ### OrderedMultiSet example
786
-
787
- ```ts
788
- import { OrderedMultiSet } from 'typescript-dsa-stl';
789
-
790
- const scores = new OrderedMultiSet<number>();
791
- scores.add(85); scores.add(92); scores.add(85); scores.add(78);
792
- console.log(scores.toArray()); // [78, 85, 85, 92]
793
- console.log(scores.count(85)); // 2
794
- scores.delete(85); // remove one 85
795
- console.log(scores.count(85)); // 1
796
- scores.deleteAll(85); // remove all 85s
797
- ```
798
-
799
- ### OrderedMultiMap example
800
-
801
- ```ts
802
- import { OrderedMultiMap } from 'typescript-dsa-stl';
803
-
804
- const index = new OrderedMultiMap<string, number>(); // term -> doc IDs
805
- index.set('typescript', 1); index.set('typescript', 3); index.set('stl', 2);
806
- console.log(index.getAll('typescript')); // [1, 3]
807
- console.log(index.get('stl')); // 2
808
- for (const [key, value] of index) {
809
- console.log(key, value); // keys in sorted order
810
- }
811
- ```
812
-
813
- ---
814
-
815
874
  ## For maintainers
816
875
 
817
876
  - **Build:** `npm run build` (also runs before `npm publish` via `prepublishOnly`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typescript-dsa-stl",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "STL-style data structures and algorithms for TypeScript: Vector, Stack, Queue, Deque (double-ended queue), List, PriorityQueue, Map, Set, sort, binarySearch, graph utilities. Use like C++ STL.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",