data-structure-typed 2.4.0 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/README.md +2 -3
  3. package/README_CN.md +0 -1
  4. package/benchmark/report.html +118 -40
  5. package/benchmark/report.json +726 -726
  6. package/dist/cjs/index.cjs +79 -21
  7. package/dist/cjs/index.cjs.map +1 -1
  8. package/dist/cjs-legacy/index.cjs +79 -21
  9. package/dist/cjs-legacy/index.cjs.map +1 -1
  10. package/dist/esm/index.mjs +79 -21
  11. package/dist/esm/index.mjs.map +1 -1
  12. package/dist/esm-legacy/index.mjs +79 -21
  13. package/dist/esm-legacy/index.mjs.map +1 -1
  14. package/dist/types/data-structures/binary-tree/tree-map.d.ts +17 -6
  15. package/dist/types/data-structures/binary-tree/tree-multi-map.d.ts +13 -5
  16. package/dist/types/data-structures/binary-tree/tree-multi-set.d.ts +12 -5
  17. package/dist/types/data-structures/binary-tree/tree-set.d.ts +15 -4
  18. package/dist/types/types/data-structures/binary-tree/tree-map.d.ts +6 -1
  19. package/dist/types/types/data-structures/binary-tree/tree-multi-set.d.ts +6 -1
  20. package/dist/types/types/data-structures/binary-tree/tree-set.d.ts +6 -1
  21. package/dist/umd/data-structure-typed.js +79 -21
  22. package/dist/umd/data-structure-typed.js.map +1 -1
  23. package/dist/umd/data-structure-typed.min.js +4 -4
  24. package/dist/umd/data-structure-typed.min.js.map +1 -1
  25. package/package.json +6 -2
  26. package/src/data-structures/binary-tree/tree-map.ts +35 -13
  27. package/src/data-structures/binary-tree/tree-multi-map.ts +41 -20
  28. package/src/data-structures/binary-tree/tree-multi-set.ts +17 -6
  29. package/src/data-structures/binary-tree/tree-set.ts +19 -6
  30. package/src/types/data-structures/binary-tree/tree-map.ts +7 -1
  31. package/src/types/data-structures/binary-tree/tree-multi-set.ts +7 -1
  32. package/src/types/data-structures/binary-tree/tree-set.ts +7 -1
  33. package/test/performance/reportor-enhanced.mjs +256 -100
  34. package/test/unit/data-structures/binary-tree/tree-map.test.ts +46 -0
  35. package/test/unit/data-structures/binary-tree/tree-multi-map.rfc.test.ts +47 -0
  36. package/test/unit/data-structures/binary-tree/tree-multi-set.test.ts +49 -0
  37. package/test/unit/data-structures/binary-tree/tree-set.test.ts +44 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "data-structure-typed",
3
- "version": "2.4.0",
3
+ "version": "2.4.1",
4
4
  "description": "Standard data structure",
5
5
  "browser": "dist/umd/data-structure-typed.min.js",
6
6
  "umd:main": "dist/umd/data-structure-typed.min.js",
@@ -104,7 +104,7 @@
104
104
  "benchmark": "^2.1.4",
105
105
  "binary-tree-typed": "^1.54.3",
106
106
  "bst-typed": "^1.54.3",
107
- "data-structure-typed": "^2.3.0",
107
+ "data-structure-typed": "^2.4.0",
108
108
  "dependency-cruiser": "^16.5.0",
109
109
  "doctoc": "^2.2.1",
110
110
  "eslint": "^9.13.0",
@@ -135,6 +135,10 @@
135
135
  "binary search tree",
136
136
  "AVL tree",
137
137
  "red black tree",
138
+ "tree map",
139
+ "tree set",
140
+ "tree multi map",
141
+ "tree multi set",
138
142
  "balanced tree",
139
143
  "binary tree",
140
144
  "BST",
@@ -17,31 +17,53 @@ import { RedBlackTree } from './red-black-tree';
17
17
  * - Iteration order is ascending by key.
18
18
  * - No node exposure: all APIs use keys/values only.
19
19
  */
20
- export class TreeMap<K = any, V = any> implements Iterable<[K, V | undefined]> {
20
+ export class TreeMap<K = any, V = any, R = [K, V]> implements Iterable<[K, V | undefined]> {
21
21
  readonly #core: RedBlackTree<K, V>;
22
22
  readonly #isDefaultComparator: boolean;
23
23
  readonly #userComparator?: Comparator<K>;
24
24
 
25
25
  /**
26
- * Create a TreeMap from an iterable of `[key, value]` entries.
26
+ * Create a TreeMap from an iterable of `[key, value]` entries or raw elements.
27
27
  *
28
- * @throws {TypeError} If any entry is not a 2-tuple-like value, or when using the default comparator
29
- * and encountering unsupported/invalid keys (e.g. `NaN`, invalid `Date`).
28
+ * @param entries - Iterable of `[key, value]` tuples, or raw elements if `toEntryFn` is provided.
29
+ * @param options - Configuration options including optional `toEntryFn` to transform raw elements.
30
+ * @throws {TypeError} If any entry is not a 2-tuple-like value (when no toEntryFn), or when using
31
+ * the default comparator and encountering unsupported/invalid keys (e.g. `NaN`, invalid `Date`).
32
+ * @example
33
+ * // Standard usage with entries
34
+ * const map = new TreeMap([['a', 1], ['b', 2]]);
35
+ *
36
+ * // Using toEntryFn to transform raw objects
37
+ * const users = [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
38
+ * const map = new TreeMap<number, User, User>(users, { toEntryFn: u => [u.id, u] });
30
39
  */
31
- constructor(entries: Iterable<[K, V | undefined]> = [], options: TreeMapOptions<K> = {}) {
40
+ constructor(
41
+ entries: Iterable<R> | Iterable<[K, V | undefined]> = [],
42
+ options: TreeMapOptions<K, V, R> = {}
43
+ ) {
32
44
  this.#userComparator = options.comparator;
45
+ const toEntryFn = options.toEntryFn;
33
46
  const comparator = options.comparator ?? TreeMap.createDefaultComparator<K>();
34
47
  this.#isDefaultComparator = options.comparator === undefined;
35
48
 
36
49
  this.#core = new RedBlackTree<K, V>([], { comparator, isMapMode: options.isMapMode });
37
50
 
38
- // Validate entries like native Map: each item must be a 2-tuple-like value.
39
- for (const item of entries as unknown as Iterable<unknown>) {
40
- if (!Array.isArray(item) || item.length < 2) {
41
- throw new TypeError('TreeMap: each entry must be a [key, value] tuple');
51
+ for (const item of entries) {
52
+ let k: K;
53
+ let v: V | undefined;
54
+
55
+ if (toEntryFn) {
56
+ // Use toEntryFn to transform raw element
57
+ [k, v] = toEntryFn(item as R);
58
+ } else {
59
+ // Validate entries like native Map: each item must be a 2-tuple-like value.
60
+ if (!Array.isArray(item) || item.length < 2) {
61
+ throw new TypeError('TreeMap: each entry must be a [key, value] tuple');
62
+ }
63
+ k = item[0] as K;
64
+ v = item[1] as V | undefined;
42
65
  }
43
- const k = item[0] as K;
44
- const v = item[1] as V | undefined;
66
+
45
67
  this.set(k, v);
46
68
  }
47
69
  }
@@ -210,10 +232,10 @@ export class TreeMap<K = any, V = any> implements Iterable<[K, V | undefined]> {
210
232
  */
211
233
  map<MK, MV>(
212
234
  callbackfn: TreeMapEntryCallback<K, V, [MK, MV], TreeMap<K, V>>,
213
- options: TreeMapOptions<MK> = {},
235
+ options: Omit<TreeMapOptions<MK, MV>, 'toEntryFn'> & { comparator?: (a: MK, b: MK) => number } = {},
214
236
  thisArg?: unknown
215
237
  ): TreeMap<MK, MV> {
216
- const out = new TreeMap<MK, MV>([], options);
238
+ const out = new TreeMap<MK, MV>([], options as TreeMapOptions<MK, MV>);
217
239
  let index = 0;
218
240
  for (const [k, v] of this) {
219
241
  const [mk, mv] = thisArg === undefined
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { Comparator, TreeMultiMapOptions } from '../../types';
10
+ import { Range } from '../../common';
10
11
  import { RedBlackTree, RedBlackTreeNode } from './red-black-tree';
11
12
  import { TreeSet } from './tree-set';
12
13
 
@@ -200,20 +201,41 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
200
201
 
201
202
  /**
202
203
  * Creates a new TreeMultiMap.
203
- * @param keysNodesEntriesOrRaws - Initial entries
204
- * @param options - Configuration options
204
+ * @param keysNodesEntriesOrRaws - Initial entries, or raw elements if `toEntryFn` is provided.
205
+ * @param options - Configuration options including optional `toEntryFn` to transform raw elements.
205
206
  * @remarks Time O(m log m), Space O(m) where m is the number of initial entries
207
+ * @example
208
+ * // Standard usage with entries
209
+ * const mmap = new TreeMultiMap([['a', ['x', 'y']], ['b', ['z']]]);
210
+ *
211
+ * // Using toEntryFn to transform raw objects
212
+ * const players = [{ score: 100, items: ['sword'] }, { score: 200, items: ['shield', 'bow'] }];
213
+ * const mmap = new TreeMultiMap(players, { toEntryFn: p => [p.score, p.items] });
206
214
  */
207
215
  constructor(
208
216
  keysNodesEntriesOrRaws: Iterable<K | [K | null | undefined, V[] | undefined] | null | undefined | R> = [],
209
- options: TreeMultiMapOptions<K, V[], R> = {} as any
217
+ options: TreeMultiMapOptions<K, V[], R> = {}
210
218
  ) {
211
- const comparator = (options as any).comparator ?? TreeSet.createDefaultComparator<K>();
212
- this.#isDefaultComparator = (options as any).comparator === undefined;
213
- this.#core = new RedBlackTree<K, V[], R>([], { ...(options as any), comparator, isMapMode: (options as any).isMapMode });
219
+ const comparator = options.comparator ?? TreeSet.createDefaultComparator<K>();
220
+ this.#isDefaultComparator = options.comparator === undefined;
221
+ const toEntryFn = options.toEntryFn;
222
+ this.#core = new RedBlackTree<K, V[], R>([], { ...options, comparator, isMapMode: options.isMapMode });
214
223
 
215
- for (const x of keysNodesEntriesOrRaws as any) {
224
+ for (const x of keysNodesEntriesOrRaws) {
216
225
  if (x === null || x === undefined) continue;
226
+
227
+ // If toEntryFn is provided, use it to transform raw element
228
+ if (toEntryFn) {
229
+ const [k, bucket] = toEntryFn(x as R);
230
+ if (k === null || k === undefined) continue;
231
+ if (bucket !== undefined) {
232
+ this.#core.set(k as K, Array.isArray(bucket) ? [...bucket] : [bucket] as V[]);
233
+ } else {
234
+ this.#core.set(k as K, [] as V[]);
235
+ }
236
+ continue;
237
+ }
238
+
217
239
  if (Array.isArray(x)) {
218
240
  const [k, bucket] = x;
219
241
  if (k === null || k === undefined) continue;
@@ -596,8 +618,8 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
596
618
  * Prints the internal tree structure (for debugging).
597
619
  * @remarks Time O(n), Space O(n)
598
620
  */
599
- print(...args: any[]): void {
600
- return (this.#core as any).print(...args);
621
+ print(): void {
622
+ this.#core.print();
601
623
  }
602
624
 
603
625
  /**
@@ -619,7 +641,7 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
619
641
  for (const [k, v] of this) {
620
642
  if (predicate(v, k, this)) filtered.push([k, v]);
621
643
  }
622
- return new TreeMultiMap<K, V, R>(filtered, { comparator: this.comparator as any });
644
+ return new TreeMultiMap<K, V, R>(filtered, { comparator: this.comparator });
623
645
  }
624
646
 
625
647
  /**
@@ -633,7 +655,7 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
633
655
  for (const [k, v] of this) {
634
656
  mapped.push(mapper(v, k, this));
635
657
  }
636
- return new TreeMultiMap<K, V2, R>(mapped, { comparator: this.comparator as any });
658
+ return new TreeMultiMap<K, V2, R>(mapped, { comparator: this.comparator });
637
659
  }
638
660
 
639
661
  /**
@@ -652,11 +674,11 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
652
674
  * Sets multiple entries at once.
653
675
  * @remarks Time O(m log n), Space O(m) where m is input size
654
676
  */
655
- setMany(keysNodesEntriesOrRaws: Iterable<any>): boolean[] {
677
+ setMany(keysNodesEntriesOrRaws: Iterable<K | [K | null | undefined, V[] | undefined]>): boolean[] {
656
678
  const results: boolean[] = [];
657
679
  for (const x of keysNodesEntriesOrRaws) {
658
680
  // Call implementation directly: entry can be K or [K, V[]] or [K, undefined]
659
- results.push(this.set(x as any, undefined as any));
681
+ results.push(this.set(x));
660
682
  }
661
683
  return results;
662
684
  }
@@ -665,12 +687,11 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
665
687
  * Searches for entries within a key range.
666
688
  * @remarks Time O(log n + k), Space O(k) where k is result size
667
689
  */
668
- rangeSearch<C extends (node: RedBlackTreeNode<K, V[]>) => any>(
669
- range: any,
670
- callback?: C,
671
- isBalanced?: any
690
+ rangeSearch<C extends (node: RedBlackTreeNode<K, V[]>) => unknown>(
691
+ range: Range<K> | [K, K],
692
+ callback?: C
672
693
  ): ReturnType<C>[] {
673
- return this.#core.rangeSearch(range, callback as any, isBalanced);
694
+ return this.#core.rangeSearch(range, callback as (node: RedBlackTreeNode<K, V[]>) => ReturnType<C>);
674
695
  }
675
696
 
676
697
  /**
@@ -678,7 +699,7 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
678
699
  * @remarks Time O(n log n), Space O(n)
679
700
  */
680
701
  clone(): TreeMultiMap<K, V, R> {
681
- return new TreeMultiMap<K, V, R>(this, { comparator: this.comparator as any, isMapMode: (this.#core as any)._isMapMode });
702
+ return new TreeMultiMap<K, V, R>(this, { comparator: this.comparator, isMapMode: this.#core.isMapMode });
682
703
  }
683
704
 
684
705
  /**
@@ -686,6 +707,6 @@ export class TreeMultiMap<K = any, V = any, R = any> implements Iterable<[K, V[]
686
707
  * @remarks Time O(1), Space O(1)
687
708
  */
688
709
  get comparator(): Comparator<K> {
689
- return (this.#core as any)._comparator;
710
+ return this.#core.comparator;
690
711
  }
691
712
  }
@@ -12,23 +12,34 @@ import type { Comparator, TreeMultiSetOptions } from '../../types';
12
12
  import { RedBlackTree } from './red-black-tree';
13
13
  import { TreeSet } from './tree-set';
14
14
 
15
- export class TreeMultiSet<K = any> implements Iterable<K> {
15
+ export class TreeMultiSet<K = any, R = K> implements Iterable<K> {
16
16
  readonly #core: RedBlackTree<K, number>;
17
17
  readonly #isDefaultComparator: boolean;
18
18
  private _size = 0; // total occurrences (sumCounts)
19
19
 
20
20
  /**
21
21
  * Creates a new TreeMultiSet.
22
- * @param elements - Initial elements to add
23
- * @param options - Configuration options
22
+ * @param elements - Initial elements to add, or raw elements if `toElementFn` is provided.
23
+ * @param options - Configuration options including optional `toElementFn` to transform raw elements.
24
24
  * @remarks Time O(m log m), Space O(m) where m is the number of initial elements
25
- */
26
- constructor(elements: Iterable<K> = [], options: TreeMultiSetOptions<K> = {}) {
25
+ * @example
26
+ * // Standard usage with elements
27
+ * const mset = new TreeMultiSet([1, 2, 2, 3]);
28
+ *
29
+ * // Using toElementFn to transform raw objects
30
+ * const items = [{ score: 100 }, { score: 200 }, { score: 100 }];
31
+ * const mset = new TreeMultiSet<number, Item>(items, { toElementFn: item => item.score });
32
+ */
33
+ constructor(elements: Iterable<R> | Iterable<K> = [], options: TreeMultiSetOptions<K, R> = {}) {
34
+ const toElementFn = options.toElementFn;
27
35
  const comparator = options.comparator ?? TreeSet.createDefaultComparator<K>();
28
36
  this.#isDefaultComparator = options.comparator === undefined;
29
37
  this.#core = new RedBlackTree<K, number>([], { comparator, isMapMode: options.isMapMode });
30
38
 
31
- for (const k of elements) this.add(k);
39
+ for (const item of elements) {
40
+ const k = toElementFn ? toElementFn(item as R) : (item as K);
41
+ this.add(k);
42
+ }
32
43
  }
33
44
 
34
45
  /**
@@ -17,26 +17,39 @@ import { RedBlackTree } from './red-black-tree';
17
17
  * - Iteration order is ascending by key.
18
18
  * - No node exposure: all APIs use keys only.
19
19
  */
20
- export class TreeSet<K = any> implements Iterable<K> {
20
+ export class TreeSet<K = any, R = K> implements Iterable<K> {
21
21
  readonly #core: RedBlackTree<K, undefined>;
22
22
  readonly #isDefaultComparator: boolean;
23
23
  readonly #userComparator?: Comparator<K>;
24
24
 
25
25
  /**
26
- * Create a TreeSet from an iterable of keys.
26
+ * Create a TreeSet from an iterable of keys or raw elements.
27
27
  *
28
+ * @param elements - Iterable of keys, or raw elements if `toElementFn` is provided.
29
+ * @param options - Configuration options including optional `toElementFn` to transform raw elements.
28
30
  * @throws {TypeError} When using the default comparator and encountering unsupported key types,
29
31
  * or invalid keys (e.g. `NaN`, invalid `Date`).
32
+ * @example
33
+ * // Standard usage with keys
34
+ * const set = new TreeSet([3, 1, 2]);
35
+ *
36
+ * // Using toElementFn to transform raw objects
37
+ * const users = [{ id: 3, name: 'Alice' }, { id: 1, name: 'Bob' }];
38
+ * const set = new TreeSet<number, User>(users, { toElementFn: u => u.id });
30
39
  */
31
- constructor(elements: Iterable<K> = [], options: TreeSetOptions<K> = {}) {
40
+ constructor(elements: Iterable<R> | Iterable<K> = [], options: TreeSetOptions<K, R> = {}) {
32
41
  this.#userComparator = options.comparator;
42
+ const toElementFn = options.toElementFn;
33
43
  const comparator = options.comparator ?? TreeSet.createDefaultComparator<K>();
34
44
  this.#isDefaultComparator = options.comparator === undefined;
35
45
 
36
46
  // RedBlackTree expects an iterable of keys/entries/nodes/raws; for TreeSet we only accept keys.
37
47
  this.#core = new RedBlackTree<K, undefined>([], { comparator, isMapMode: options.isMapMode });
38
48
 
39
- for (const k of elements) this.add(k);
49
+ for (const item of elements) {
50
+ const k = toElementFn ? toElementFn(item as R) : item as K;
51
+ this.add(k);
52
+ }
40
53
  }
41
54
 
42
55
  /**
@@ -194,10 +207,10 @@ export class TreeSet<K = any> implements Iterable<K> {
194
207
  */
195
208
  map<MK>(
196
209
  callbackfn: TreeSetElementCallback<K, MK, TreeSet<K>>,
197
- options: TreeSetOptions<MK> = {},
210
+ options: Omit<TreeSetOptions<MK>, 'toElementFn'> & { comparator?: (a: MK, b: MK) => number } = {},
198
211
  thisArg?: unknown
199
212
  ): TreeSet<MK> {
200
- const out = new TreeSet<MK>([], options);
213
+ const out = new TreeSet<MK>([], options as TreeSetOptions<MK>);
201
214
  let index = 0;
202
215
  for (const v of this) {
203
216
  const mk = thisArg === undefined
@@ -1,6 +1,6 @@
1
1
  import type { Comparator } from '../../common';
2
2
 
3
- export interface TreeMapOptions<K> {
3
+ export interface TreeMapOptions<K, V, R = [K, V]> {
4
4
  comparator?: Comparator<K>;
5
5
 
6
6
  /**
@@ -10,6 +10,12 @@ export interface TreeMapOptions<K> {
10
10
  * - `false`: store values on tree nodes (Node Mode).
11
11
  */
12
12
  isMapMode?: boolean;
13
+
14
+ /**
15
+ * Transform raw elements into `[key, value]` entries.
16
+ * When provided, the constructor accepts `Iterable<R>` instead of `Iterable<[K, V]>`.
17
+ */
18
+ toEntryFn?: (rawElement: R) => [K, V];
13
19
  }
14
20
 
15
21
  export type TreeMapRangeOptions = {
@@ -1,6 +1,6 @@
1
1
  import type { Comparator } from '../../common';
2
2
 
3
- export interface TreeMultiSetOptions<K> {
3
+ export interface TreeMultiSetOptions<K, R = K> {
4
4
  comparator?: Comparator<K>;
5
5
 
6
6
  /**
@@ -10,4 +10,10 @@ export interface TreeMultiSetOptions<K> {
10
10
  * - `false`: Node Mode.
11
11
  */
12
12
  isMapMode?: boolean;
13
+
14
+ /**
15
+ * Transform raw elements into keys.
16
+ * When provided, the constructor accepts `Iterable<R>` instead of `Iterable<K>`.
17
+ */
18
+ toElementFn?: (rawElement: R) => K;
13
19
  }
@@ -1,6 +1,6 @@
1
1
  import type { Comparator } from '../../common';
2
2
 
3
- export interface TreeSetOptions<K> {
3
+ export interface TreeSetOptions<K, R = K> {
4
4
  comparator?: Comparator<K>;
5
5
 
6
6
  /**
@@ -10,6 +10,12 @@ export interface TreeSetOptions<K> {
10
10
  * - `false`: store values on tree nodes (Node Mode).
11
11
  */
12
12
  isMapMode?: boolean;
13
+
14
+ /**
15
+ * Transform raw elements into keys.
16
+ * When provided, the constructor accepts `Iterable<R>` instead of `Iterable<K>`.
17
+ */
18
+ toElementFn?: (rawElement: R) => K;
13
19
  }
14
20
 
15
21
  export type TreeSetRangeOptions = {