@thi.ng/trie 1.1.28 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2025-08-04T08:45:04Z
3
+ - **Last updated**: 2025-08-11T16:41:24Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
@@ -11,6 +11,27 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
11
11
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
12
12
  and/or version bumps of transitive dependencies.
13
13
 
14
+ # [2.0.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/trie@2.0.0) (2025-08-11)
15
+
16
+ #### 🛑 Breaking changes
17
+
18
+ - major update `TrieMap` & `MultiTrie` ([d7f4a12](https://github.com/thi-ng/umbrella/commit/d7f4a12))
19
+ - BREAKING CHANGE: major update `TrieMap` & `MultiTrie`
20
+ - update `MultiTrie` to only support array-based keys
21
+ - switch internals to using Map for storing branches
22
+ - update arg types in all methods
23
+ - add `.toJSON()` method to support serialization
24
+ - rename `MultiTrieOpts.vals` => `.values`
25
+ - add/update/dedupe iterators in both impls
26
+ - replace `.suffixes()` iterator in both impls w/ extra args passed to `.keys()`
27
+ - add `defTrieMapFromJSON()` and `defMultiTrieFromJSON()`
28
+ - add/update tests
29
+
30
+ #### ♻️ Refactoring
31
+
32
+ - update internals ([065cc27](https://github.com/thi-ng/umbrella/commit/065cc27))
33
+ - remove obsolete size/count in MultiTrie
34
+
14
35
  ### [1.1.10](https://github.com/thi-ng/umbrella/tree/@thi.ng/trie@1.1.10) (2025-01-14)
15
36
 
16
37
  #### ♻️ Refactoring
package/README.md CHANGED
@@ -27,7 +27,7 @@
27
27
 
28
28
  ## About
29
29
 
30
- Trie-based map data structure with prefix search/query support.
30
+ Trie-based ES6-like Map data structures with prefix search/query support.
31
31
 
32
32
  This package contains functionality which was previously part of and has been
33
33
  extracted from the [@thi.ng/associative](https://thi.ng/associative) package.
@@ -42,9 +42,10 @@ prefix, longest matching prefix queries etc.
42
42
  The implementations here too feature ES6 Map-like API, similar to other types in
43
43
  this package, with some further trie-specific additions.
44
44
 
45
- ```ts
46
- import { defTrieMap } from "@thi.ng/associative";
45
+ ```ts tangle:export/readme-1.ts
46
+ import { defTrieMap } from "@thi.ng/trie";
47
47
 
48
+ // construct trie from given key-value pairs (optional)
48
49
  const trie = defTrieMap([
49
50
  ["hey", "en"],
50
51
  ["hello", "en"],
@@ -55,42 +56,55 @@ const trie = defTrieMap([
55
56
  ["hej", "se"],
56
57
  ]);
57
58
 
58
- trie.knownPrefix("hole")
59
+ // find longest known prefix given key
60
+ console.log(trie.knownPrefix("hole"));
59
61
  // "hol"
60
62
 
61
- [...trie.suffixes("he")]
62
- // [ "j", "llo", "y" ]
63
+ // all known keys
64
+ console.log([...trie.keys()])
65
+ // [ "hold", "hola", "hallo", "hej", "hello", "hey" ]
63
66
 
64
- // w/ prefix included
65
- [...trie.suffixes("he", true)]
67
+ // all keys starting with given prefix
68
+ console.log([...trie.keys("he")])
66
69
  // [ "hej", "hello", "hey" ]
70
+
71
+ // suffixes of given key only
72
+ console.log([...trie.keys("he", false)])
73
+ // [ "j", "llo", "y" ]
74
+
75
+ // values of keys starting with prefix
76
+ console.log([...trie.values("hol")]);
77
+ // [ "en", "es" ]
67
78
  ```
68
79
 
69
80
  ### MultiTrie
70
81
 
71
- The `MultiTrie` is similar to `TrieMap`, but supports array-like keys and
82
+ The `MultiTrie` is similar to `TrieMap`, but uses array-like keys and supports
72
83
  multiple values per key. Values are stored in sets whose implementation can be
73
- configured via ctor options.
84
+ configured via ctor options (e.g. using custom ES6-like Sets with value-based
85
+ equality semantics from the [thi.ng/associative](https://thi.ng/associative)
86
+ package).
74
87
 
75
- ```ts
76
- import { defMultiTrie } from "@thi.ng/associative";
88
+ ```ts tangle:export/readme-2.ts
89
+ import { defMultiTrie } from "@thi.ng/trie";
90
+ import { ArraySet } from "@thi.ng/associative";
77
91
 
78
- // init w/ custom value set type (here only for illustration)
79
- const t = defMultiTrie<string[], string>(null, { vals: () => new ArraySet() });
92
+ // init w/ custom value set type (here purely for illustration)
93
+ const t = defMultiTrie<string, string>(null, { values: () => new ArraySet() });
80
94
 
81
95
  t.add("to be or not to be".split(" "), 1);
82
96
  t.add("to be or not to be".split(" "), 2);
83
97
  t.add("to be and to live".split(" "), 3);
84
98
 
85
- t.get("to be or not to be".split(" "))
99
+ console.log(t.get("to be or not to be".split(" ")))
86
100
  // Set(2) { 1, 2 }
87
101
 
88
- t.knownPrefix(["to", "be", "not"]);
102
+ console.log(t.knownPrefix(["to", "be", "not"]));
89
103
  // [ "to", "be" ]
90
104
 
91
- // auto-complete w/ custom separator between words
92
- [...t.suffixes(["to", "be"], false, "/")]
93
- // [ "and/to/live", "or/not/to/be" ]
105
+ // suffixes for given prefix
106
+ console.log([...t.keys(["to", "be"], false)]);
107
+ // [["and", "to", "live"], ["or", "not", "to", "be"]]
94
108
  ```
95
109
 
96
110
  ## Status
@@ -129,7 +143,7 @@ For Node.js REPL:
129
143
  const trie = await import("@thi.ng/trie");
130
144
  ```
131
145
 
132
- Package sizes (brotli'd, pre-treeshake): ESM: 1.01 KB
146
+ Package sizes (brotli'd, pre-treeshake): ESM: 1.14 KB
133
147
 
134
148
  ## Dependencies
135
149
 
package/multi-trie.d.ts CHANGED
@@ -1,39 +1,54 @@
1
- import type { Fn0, IObjectOf, Maybe, Nullable, Pair } from "@thi.ng/api";
1
+ import type { Fn0, Fn2, Maybe, Nullable, Pair } from "@thi.ng/api";
2
2
  export interface MultiTrieOpts<V> {
3
3
  /**
4
- * Custom value set factory (e.g. for using `Set` implementations from the
4
+ * Custom value set factory (e.g. `Set` implementations from the
5
5
  * [thi.ng/associative](https://thi.ng/associative) package). Uses native
6
6
  * ES6 Set by default.
7
7
  */
8
- vals: Fn0<Set<V>>;
8
+ values: Fn0<Set<V>>;
9
9
  }
10
- export declare class MultiTrie<K extends ArrayLike<any>, V> {
10
+ /**
11
+ * Multi-Map-like trie implementation for array-based keys and supporting
12
+ * multiple unique values per key.
13
+ */
14
+ export declare class MultiTrie<K, V> {
11
15
  protected opts?: Partial<MultiTrieOpts<V>> | undefined;
12
- protected next: IObjectOf<MultiTrie<K, V>>;
13
- protected vals?: Set<V>;
14
- protected n: number;
15
- constructor(pairs?: Nullable<Iterable<Pair<K, V>>>, opts?: Partial<MultiTrieOpts<V>> | undefined);
16
- [Symbol.iterator](): Generator<(string | V)[], void, unknown>;
17
- keys(sep?: string, prefix?: string): Generator<string, void, unknown>;
18
- values(): Generator<V, void, unknown>;
19
- suffixes(prefix: K, withPrefix?: boolean, sep?: string): Generator<string, void, unknown>;
16
+ next: Map<K, MultiTrie<K, V>>;
17
+ vals?: Set<V>;
18
+ constructor(pairs?: Nullable<Iterable<Pair<K[], V>>>, opts?: Partial<MultiTrieOpts<V>> | undefined);
19
+ [Symbol.iterator](): Generator<Pair<K, V>, void, any>;
20
+ keys(prefix?: K[], includePrefix?: boolean): Generator<K[], void, any>;
21
+ values(prefix?: K[]): Generator<V, void, any>;
22
+ entries(prefix?: K[], includePrefix?: boolean): Generator<Pair<K, V>, void, any>;
20
23
  clear(): void;
21
- has(key: K): boolean;
22
- hasPrefix(prefix: K): boolean;
23
- get(key: K): Maybe<Set<V>>;
24
- find(key: K): MultiTrie<K, V> | undefined;
24
+ has(key: K[]): boolean;
25
+ hasPrefix(prefix: K[]): boolean;
26
+ get(key: K[]): Maybe<Set<V>>;
27
+ find(key: K[]): MultiTrie<K, V> | undefined;
25
28
  /**
26
29
  * Returns longest known prefix for `key` as array. If array is
27
30
  * empty, the given key has no partial matches.
28
31
  *
29
32
  * @param key -
30
33
  */
31
- knownPrefix(key: K): K[];
32
- hasKnownPrefix(key: K): boolean;
33
- add(key: K, val: V): void;
34
- into(pairs: Iterable<[K, V]>): void;
35
- delete(prefix: K, val?: V): boolean;
36
- protected queueChildren(queue: [string, MultiTrie<any, any>][], prefix: string, sep?: string): void;
34
+ knownPrefix(key: K[]): K[];
35
+ hasKnownPrefix(key: K[]): boolean;
36
+ add(key: K[], val: V): void;
37
+ into(pairs: Iterable<[K[], V]>): void;
38
+ delete(prefix: K[], val?: V): boolean;
39
+ toJSON(): SerializedMultiTrie<V>;
40
+ protected iterate<T>(fn: Fn2<K[], MultiTrie<K, V>, Iterable<T>>, prefix?: K[], includePrefix?: boolean): Generator<T, void, any>;
37
41
  }
38
- export declare const defMultiTrie: <K extends ArrayLike<any>, V>(pairs?: Iterable<Pair<K, V>>, opts?: Partial<MultiTrieOpts<V>>) => MultiTrie<K, V>;
42
+ export declare const defMultiTrie: <K, V>(pairs?: Nullable<Iterable<Pair<K[], V>>>, opts?: Partial<MultiTrieOpts<V>>) => MultiTrie<K, V>;
43
+ export type SerializedMultiTrie<V> = {
44
+ next: Record<string, SerializedMultiTrie<V>>;
45
+ vals?: any[];
46
+ };
47
+ /**
48
+ * Reconstruct a {@link MultiTrie} from serialized JSON.
49
+ *
50
+ * @param src
51
+ * @param opts
52
+ */
53
+ export declare const defMultiTrieFromJSON: <V>(src: SerializedMultiTrie<V>, opts?: Partial<MultiTrieOpts<V>>) => MultiTrie<string, V>;
39
54
  //# sourceMappingURL=multi-trie.d.ts.map
package/multi-trie.js CHANGED
@@ -3,54 +3,28 @@ class MultiTrie {
3
3
  this.opts = opts;
4
4
  pairs && this.into(pairs);
5
5
  }
6
- next = {};
6
+ next = /* @__PURE__ */ new Map();
7
7
  vals;
8
- n = 0;
9
- *[Symbol.iterator]() {
10
- const queue = [["", this]];
11
- while (queue.length) {
12
- const [prefix, node] = queue.pop();
13
- if (node.vals) {
14
- for (let v of node.vals) yield [prefix, v];
15
- } else {
16
- node.queueChildren(queue, prefix);
17
- }
18
- }
8
+ [Symbol.iterator]() {
9
+ return this.iterate(
10
+ (key, node) => [...node.vals].map((v) => [key, v])
11
+ );
19
12
  }
20
- *keys(sep = "", prefix = "") {
21
- const queue = [[prefix, this]];
22
- while (queue.length) {
23
- const [key, node] = queue.pop();
24
- if (node.vals) {
25
- yield key;
26
- } else {
27
- node.queueChildren(queue, key, sep);
28
- }
29
- }
13
+ keys(prefix = [], includePrefix = true) {
14
+ return this.iterate((key) => [key], prefix, includePrefix);
30
15
  }
31
- *values() {
32
- const queue = [this];
33
- while (queue.length) {
34
- const node = queue.pop();
35
- if (node.vals) {
36
- yield* node.vals;
37
- } else {
38
- queue.push(...Object.values(node.next));
39
- }
40
- }
16
+ values(prefix) {
17
+ return this.iterate((_, node) => node.vals, prefix);
41
18
  }
42
- *suffixes(prefix, withPrefix = false, sep = "") {
43
- const node = this.find(prefix);
44
- if (node) {
45
- yield* node.keys(
46
- sep,
47
- withPrefix ? Array.isArray(prefix) ? prefix.join(sep) : prefix.toString() : ""
48
- );
49
- }
19
+ entries(prefix, includePrefix = true) {
20
+ return this.iterate(
21
+ (key, node) => [...node.vals].map((v) => [key, v]),
22
+ prefix,
23
+ includePrefix
24
+ );
50
25
  }
51
26
  clear() {
52
- this.next = {};
53
- this.n = 0;
27
+ this.next.clear();
54
28
  this.vals = void 0;
55
29
  }
56
30
  has(key) {
@@ -66,7 +40,7 @@ class MultiTrie {
66
40
  find(key) {
67
41
  let node = this;
68
42
  for (let i = 0, n = key.length; i < n; i++) {
69
- node = node.next[key[i].toString()];
43
+ node = node.next.get(key[i]);
70
44
  if (!node) return;
71
45
  }
72
46
  return node;
@@ -81,8 +55,8 @@ class MultiTrie {
81
55
  let node = this;
82
56
  const prefix = [];
83
57
  for (let i = 0, n = key.length; i < n; i++) {
84
- const k = key[i].toString();
85
- const next = node.next[k];
58
+ const k = key[i];
59
+ const next = node.next.get(k);
86
60
  if (!next) break;
87
61
  prefix.push(k);
88
62
  node = next;
@@ -95,13 +69,18 @@ class MultiTrie {
95
69
  add(key, val) {
96
70
  let node = this;
97
71
  for (let i = 0, n = key.length; i < n; i++) {
98
- const k = key[i].toString();
99
- const next = node.next[k];
100
- node = !next ? (node.n++, node.next[k] = new MultiTrie(null, this.opts)) : next;
72
+ const k = key[i];
73
+ const next = node.next.get(k);
74
+ if (!next) {
75
+ const newNode = new MultiTrie(null, this.opts);
76
+ node.next.set(k, newNode);
77
+ node = newNode;
78
+ } else {
79
+ node = next;
80
+ }
101
81
  }
102
82
  if (!node.vals) {
103
- const ctor = this.opts?.vals;
104
- node.vals = ctor ? ctor() : /* @__PURE__ */ new Set();
83
+ node.vals = this.opts?.values?.() ?? /* @__PURE__ */ new Set();
105
84
  }
106
85
  node.vals.add(val);
107
86
  }
@@ -118,10 +97,10 @@ class MultiTrie {
118
97
  let i = 0;
119
98
  let node = this;
120
99
  for (; i < n; i++) {
121
- const k = prefix[i].toString();
100
+ const k = prefix[i];
122
101
  key.push(k);
123
102
  path.push(node);
124
- node = node.next[k];
103
+ node = node.next.get(k);
125
104
  if (!node) return false;
126
105
  }
127
106
  if (val !== void 0) {
@@ -134,22 +113,54 @@ class MultiTrie {
134
113
  }
135
114
  }
136
115
  while (node = path[--i]) {
137
- delete node.next[key[i]];
138
- if (--node.n) break;
116
+ node.next.delete(key[i]);
117
+ if (node.next.size) break;
139
118
  }
140
119
  return true;
141
120
  }
142
- queueChildren(queue, prefix, sep = "") {
143
- prefix = prefix.length ? prefix + sep : prefix;
144
- queue.push(
145
- ...Object.keys(this.next).map(
146
- (k) => [prefix + k, this.next[k]]
147
- )
148
- );
121
+ toJSON() {
122
+ return {
123
+ next: [...this.next].reduce(
124
+ (acc, [k, v]) => (acc[k] = v.toJSON(), acc),
125
+ {}
126
+ ),
127
+ vals: this.vals ? [...this.vals] : void 0
128
+ };
129
+ }
130
+ *iterate(fn, prefix = [], includePrefix = true) {
131
+ const root = this.find(prefix);
132
+ if (!root) return;
133
+ const queue = [
134
+ [includePrefix ? prefix : [], root]
135
+ ];
136
+ while (queue.length) {
137
+ const [key, node] = queue.pop();
138
+ if (node.vals) yield* fn(key, node);
139
+ queue.push(
140
+ ...[...node.next].map(
141
+ ([k, v]) => [key.concat(k), v]
142
+ )
143
+ );
144
+ }
149
145
  }
150
146
  }
151
147
  const defMultiTrie = (pairs, opts) => new MultiTrie(pairs, opts);
148
+ const defMultiTrieFromJSON = (src, opts) => {
149
+ const res = defMultiTrie(null, opts);
150
+ const queue = [[[], src]];
151
+ while (queue.length) {
152
+ const [key, node] = queue.pop();
153
+ if (node.vals) {
154
+ for (let v of node.vals) res.add(key, v);
155
+ }
156
+ for (let [k, child] of Object.entries(node.next)) {
157
+ queue.push([[...key, k], child]);
158
+ }
159
+ }
160
+ return res;
161
+ };
152
162
  export {
153
163
  MultiTrie,
154
- defMultiTrie
164
+ defMultiTrie,
165
+ defMultiTrieFromJSON
155
166
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@thi.ng/trie",
3
- "version": "1.1.28",
4
- "description": "Trie-based map data structure with prefix search/query support",
3
+ "version": "2.0.0",
4
+ "description": "Trie-based ES6-like Map data structures with prefix search/query support",
5
5
  "type": "module",
6
6
  "module": "./index.js",
7
7
  "typings": "./index.d.ts",
@@ -39,7 +39,7 @@
39
39
  "tool:tangle": "../../node_modules/.bin/tangle src/**/*.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@thi.ng/api": "^8.12.0"
42
+ "@thi.ng/api": "^8.12.1"
43
43
  },
44
44
  "devDependencies": {
45
45
  "esbuild": "^0.25.8",
@@ -47,13 +47,18 @@
47
47
  "typescript": "^5.9.2"
48
48
  },
49
49
  "keywords": [
50
+ "associative",
50
51
  "datastructure",
51
- "equality",
52
52
  "iterator",
53
53
  "keys",
54
54
  "map",
55
55
  "prefix",
56
56
  "query",
57
+ "search",
58
+ "set",
59
+ "string",
60
+ "text",
61
+ "tree",
57
62
  "trie",
58
63
  "typescript"
59
64
  ],
@@ -88,5 +93,5 @@
88
93
  ],
89
94
  "year": 2020
90
95
  },
91
- "gitHead": "d4e2e72dcdadf26da3400900f04f9eb0ebeb0a5b\n"
96
+ "gitHead": "f6ebc1302dc9211d588293aa0897173c6e54f4e5\n"
92
97
  }
package/trie-map.d.ts CHANGED
@@ -1,13 +1,13 @@
1
- import type { IObjectOf, Maybe, Pair } from "@thi.ng/api";
1
+ import type { Fn2, IObjectOf, Maybe, Pair } from "@thi.ng/api";
2
2
  export declare class TrieMap<T> {
3
- protected next: IObjectOf<TrieMap<T>>;
4
- protected val?: T;
3
+ next: IObjectOf<TrieMap<T>>;
4
+ val?: T;
5
5
  protected n: number;
6
6
  constructor(pairs?: Iterable<Pair<string, T>>);
7
- [Symbol.iterator](): Generator<(string | (T & ({} | null)))[], void, unknown>;
8
- keys(prefix?: string): Generator<string, void, unknown>;
9
- values(): Generator<T & ({} | null), void, unknown>;
10
- suffixes(prefix: string, withPrefix?: boolean): Generator<string, void, unknown>;
7
+ [Symbol.iterator](): Generator<Pair<string, T>, void, unknown>;
8
+ keys(prefix?: string, includePrefix?: boolean): Generator<string, void, unknown>;
9
+ values(prefix?: string): Generator<NonNullable<T>, void, unknown>;
10
+ entries(prefix?: string, includePrefix?: boolean): Generator<Pair<string, T>, void, unknown>;
11
11
  clear(): void;
12
12
  has(key: string): boolean;
13
13
  hasPrefix(prefix: string): boolean;
@@ -24,7 +24,18 @@ export declare class TrieMap<T> {
24
24
  set(key: string, val: T): void;
25
25
  into(pairs: Iterable<Pair<string, T>>): void;
26
26
  delete(prefix: string): boolean;
27
- protected queueChildren(queue: [string, TrieMap<T>][], prefix: string): void;
27
+ protected iterate<V>(fn: Fn2<string, TrieMap<T>, V>, prefix?: string, includePrefix?: boolean): Generator<V, void, unknown>;
28
28
  }
29
29
  export declare const defTrieMap: <T>(pairs?: Iterable<Pair<string, T>>) => TrieMap<T>;
30
+ export type SerializedTrieMap<T> = {
31
+ next: IObjectOf<SerializedTrieMap<T>>;
32
+ val?: T;
33
+ };
34
+ /**
35
+ * Reconstruct a {@link MultiTrie} from serialized JSON.
36
+ *
37
+ * @param src
38
+ * @param opts
39
+ */
40
+ export declare const defTrieMapFromJSON: <V>(src: SerializedTrieMap<V>) => TrieMap<V>;
30
41
  //# sourceMappingURL=trie-map.d.ts.map
package/trie-map.js CHANGED
@@ -5,44 +5,21 @@ class TrieMap {
5
5
  constructor(pairs) {
6
6
  pairs && this.into(pairs);
7
7
  }
8
- *[Symbol.iterator]() {
9
- const queue = [["", this]];
10
- while (queue.length) {
11
- const [prefix, node] = queue.pop();
12
- if (node.val !== void 0) {
13
- yield [prefix, node.val];
14
- } else {
15
- node.queueChildren(queue, prefix);
16
- }
17
- }
8
+ [Symbol.iterator]() {
9
+ return this.iterate((key, node) => [key, node.val]);
18
10
  }
19
- *keys(prefix = "") {
20
- const queue = [[prefix, this]];
21
- while (queue.length) {
22
- const [key, node] = queue.pop();
23
- if (node.val !== void 0) {
24
- yield key;
25
- } else {
26
- node.queueChildren(queue, key);
27
- }
28
- }
11
+ keys(prefix, includePrefix = true) {
12
+ return this.iterate((key) => key, prefix, includePrefix);
29
13
  }
30
- *values() {
31
- const queue = [this];
32
- while (queue.length) {
33
- const node = queue.pop();
34
- if (node.val !== void 0) {
35
- yield node.val;
36
- } else {
37
- queue.push(...Object.values(node.next));
38
- }
39
- }
14
+ values(prefix) {
15
+ return this.iterate((_, node) => node.val, prefix);
40
16
  }
41
- *suffixes(prefix, withPrefix = false) {
42
- const node = this.find(prefix);
43
- if (node) {
44
- yield* node.keys(withPrefix ? prefix : "");
45
- }
17
+ entries(prefix, includePrefix = true) {
18
+ return this.iterate(
19
+ (key, node) => [key, node.val],
20
+ prefix,
21
+ includePrefix
22
+ );
46
23
  }
47
24
  clear() {
48
25
  this.next = {};
@@ -122,16 +99,38 @@ class TrieMap {
122
99
  }
123
100
  return true;
124
101
  }
125
- queueChildren(queue, prefix) {
126
- queue.push(
127
- ...Object.keys(this.next).map(
128
- (k) => [prefix + k, this.next[k]]
129
- )
130
- );
102
+ *iterate(fn, prefix = "", includePrefix = true) {
103
+ const root = this.find(prefix);
104
+ if (!root) return;
105
+ const queue = [
106
+ [includePrefix ? prefix : "", root]
107
+ ];
108
+ while (queue.length) {
109
+ const [key, node] = queue.pop();
110
+ if (node.val !== void 0) yield fn(key, node);
111
+ queue.push(
112
+ ...Object.entries(node.next).map(
113
+ ([k, v]) => [key + k, v]
114
+ )
115
+ );
116
+ }
131
117
  }
132
118
  }
133
119
  const defTrieMap = (pairs) => new TrieMap(pairs);
120
+ const defTrieMapFromJSON = (src) => {
121
+ const res = defTrieMap();
122
+ const queue = [["", src]];
123
+ while (queue.length) {
124
+ const [key, node] = queue.pop();
125
+ if (node.val) res.set(key, node.val);
126
+ for (let [k, child] of Object.entries(node.next)) {
127
+ queue.push([key + k, child]);
128
+ }
129
+ }
130
+ return res;
131
+ };
134
132
  export {
135
133
  TrieMap,
136
- defTrieMap
134
+ defTrieMap,
135
+ defTrieMapFromJSON
137
136
  };