@linkdotnet/stringoperations
Version:
Collection of string utilities. Edit-Distances, Search and Data structures. Offers for example trie, levenshtein distance.
131 lines (129 loc) • 4.42 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Trie = void 0;
class Trie {
// eslint-disable-next-line no-useless-constructor
constructor(ignoreCase = false) {
this.ignoreCase = ignoreCase;
this.children = {};
this.isLeaf = false;
}
/**
* Adds a word to the trie
* @param word Word to add to the trie
*/
addWord(word) {
let current = this.children;
for (let i = 0; i < word.length; i++) {
const currentCharacter = this.ignoreCase ? word[i].toUpperCase() : word[i];
const node = this.createOrGetNode(currentCharacter, current);
current = node.children;
if (i === word.length - 1) {
node.isLeaf = true;
}
}
}
/**
* Check whether a word is contained in the trie
* @param word Word to check whether it exists in the trie
* @returns Returns true when the word is contained, otherwise false
*/
contains(word) {
if (word.length === 0) {
return false;
}
const node = this.findNode(word);
return node !== undefined && node.isLeaf;
}
/**
* Determines whether this trie starts with the specified character
* @param text Character to compare
* @returns True, when the text matches the beginning of the trie, otherwise false
*/
startsWith(text) {
if (text.length === 0) {
return false;
}
return this.findNode(text) !== undefined;
}
/**
* Returns all words in the trie which starts with the given prefix
* @param prefix Starting sequence which all returned words have to match
* @returns All words in the trie which start with the given prefix
*/
getWordsWithPrefix(prefix) {
const node = this.findNode(prefix);
if (!node) {
return [];
}
const results = [];
const prefixes = this.stringToCharArray(prefix);
this.collect(node, prefixes, results);
return results;
}
/**
* Deletes the key out of the trie. When multiple words matches this key all of them get deleted
* @param key Word to delete
* @remarks If trie contains out of 'Hello', 'Helsinki' and delete('Hel') is called, the trie is empty
*/
delete(key) {
Trie.deleteInternal(this, key, 0);
}
createOrGetNode(character, children) {
let node;
if (children[character] !== undefined) {
node = children[character];
}
else {
node = new Trie(this.ignoreCase);
children[character] = node;
}
return node;
}
findNode(word) {
let children = this.children;
let currentNode;
for (let i = 0; i < word.length; i++) {
const currentCharacter = this.ignoreCase ? word[i].toUpperCase() : word[i];
if (children[currentCharacter] !== undefined) {
currentNode = children[currentCharacter];
children = currentNode.children;
}
else {
return undefined;
}
}
return currentNode;
}
stringToCharArray(prefix) {
const prefixes = [];
for (let i = 0; i < prefix.length; i++) {
prefixes.push(prefix[i]);
}
return prefixes;
}
collect(node, prefix, results) {
if (Object.keys(node.children).length === 0) {
results.push(prefix.join(''));
return;
}
Object.keys(node.children).forEach(char => {
prefix.push(char);
this.collect(node.children[char], prefix, results);
prefix.pop();
});
}
static deleteInternal(node, word, index) {
if (index === word.length) {
node = null;
}
else {
const char = word[index];
if ((node === null || node === void 0 ? void 0 : node.children[char]) && Trie.deleteInternal(node.children[char], word, index + 1)) {
delete node.children[char];
}
}
return node === null || (node !== null && Object.keys(node.children).length === 0);
}
}
exports.Trie = Trie;