|
| 1 | +// represents the leaf node's key from its parent, meaning thats the end of a word |
| 2 | +const LEAF_NODE_NOTATION = "$" |
| 3 | + |
1 | 4 | class Node {
|
2 | 5 | edges: Map<string, Node> = new Map()
|
3 |
| - prefix: string |
4 | 6 | isWordEnd: boolean = false
|
5 | 7 |
|
6 |
| - constructor(prefix = "") { |
7 |
| - this.prefix = prefix |
8 |
| - } |
9 |
| - |
10 | 8 | // adds the char to the edge if not present, and returns the edge
|
11 | 9 | // or returns the edge if already present
|
12 | 10 | addChar = (char: string): Node => {
|
13 | 11 | if (this.edges.has(char)) {
|
14 | 12 | return this.edges.get(char)!
|
15 | 13 | }
|
16 |
| - const node = new Node(this.prefix + char) |
| 14 | + const node = new Node() |
17 | 15 | this.edges.set(char, node)
|
18 | 16 | return node
|
19 | 17 | }
|
20 | 18 |
|
21 | 19 | addLeaf = (): Node => {
|
22 |
| - const node = new Node(this.prefix) |
23 |
| - this.edges.set(this.prefix, node) |
| 20 | + const node = new Node() |
| 21 | + this.edges.set(LEAF_NODE_NOTATION, node) |
24 | 22 | node.isWordEnd = true
|
25 | 23 | return node
|
26 | 24 | }
|
@@ -67,49 +65,52 @@ export default class Trie {
|
67 | 65 | }
|
68 | 66 |
|
69 | 67 | // walk the trie until we find the last matching char
|
70 |
| - _walkTrieForBestMatch = (word: string): { node: Node, length: number } => { |
| 68 | + _walkTrieForBestMatch = (word: string): { node: Node, length: number, stringMatched: string } => { |
71 | 69 | let currentNode: Node = this._head
|
| 70 | + let stringMatched = "" |
72 | 71 | let i = 0;
|
73 | 72 | for (;i < word.length; i++) {
|
74 | 73 | const char = word.charAt(i)
|
75 | 74 | const node = currentNode.getEdge(char)
|
76 | 75 | if (node === undefined) break;
|
77 | 76 | else {
|
| 77 | + stringMatched = stringMatched + char |
78 | 78 | currentNode = node
|
79 | 79 | }
|
80 | 80 | }
|
81 | 81 | // the head node has a empty char, so the length will be equal to index that we have walked so far
|
82 |
| - return { node: currentNode, length: i } |
| 82 | + return { node: currentNode, length: i, stringMatched } |
83 | 83 | }
|
84 | 84 |
|
85 |
| - // send empty word to accept partial matches |
86 |
| - _walkNodeToGetWords = (node: Node, patterns: Set<string>, word = "") => { |
| 85 | + // send empty word to accept partial matches, parent is used for recursion |
| 86 | + _walkNodeToGetWords = (node: Node, patterns: Set<string>, word = "", parent = "") => { |
87 | 87 | if (node.isWordEnd) {
|
88 | 88 | // the if check is to avoid partial matches
|
89 |
| - // for example, when we check for "partyy" we should not return "part" |
90 |
| - if (word.length <= node.prefix.length) { |
91 |
| - patterns.add(node.prefix) |
| 89 | + // for example, when we check for "partyy" we should not return "part" or "party" |
| 90 | + if (word.length <= parent.length) { |
| 91 | + patterns.add(parent) |
92 | 92 | }
|
93 | 93 | return
|
94 | 94 | }
|
95 | 95 | for (const [key, value] of node.edges) {
|
96 | 96 | // the next constructed words' predecessor must have have the last predecssor + current key
|
97 | 97 | // like "lo" + "r" then onto "lor" then "e" and "m"
|
98 |
| - this._walkNodeToGetWords(value, patterns, word) |
| 98 | + const nextParent = key === LEAF_NODE_NOTATION ? parent : (parent + key) // when key is "$", dont add it to parent string |
| 99 | + this._walkNodeToGetWords(value, patterns, word, nextParent) |
99 | 100 | }
|
100 | 101 | }
|
101 | 102 |
|
102 | 103 | hasWord = (word: string): boolean => {
|
103 | 104 | const { node, length } = this._walkTrieForBestMatch(word)
|
104 |
| - const endNode = node.getEdge(word) |
| 105 | + const endNode = node.getEdge(LEAF_NODE_NOTATION) |
105 | 106 | const hasEndNode = endNode !== undefined && endNode.isWordEnd
|
106 | 107 | return (length === word.length && hasEndNode)
|
107 | 108 | }
|
108 | 109 |
|
109 | 110 | getSuggestions = (word: string, acceptPartialMatch = false): Set<string> => {
|
110 |
| - const { node } = this._walkTrieForBestMatch(word) |
| 111 | + const { node, stringMatched } = this._walkTrieForBestMatch(word) |
111 | 112 | const patterns: Set<string> = new Set()
|
112 |
| - this._walkNodeToGetWords(node, patterns, acceptPartialMatch ? "" : word) |
| 113 | + this._walkNodeToGetWords(node, patterns, acceptPartialMatch ? "" : word, stringMatched) |
113 | 114 | return patterns
|
114 | 115 | }
|
115 | 116 |
|
|
0 commit comments