Skip to content

Commit

Permalink
Merge pull request #27019 from software-mansion-labs/ts-migration/Trie
Browse files Browse the repository at this point in the history
[TS migration] Migrate 'Trie' lib to TypeScript
  • Loading branch information
cristipaval authored Sep 11, 2023
2 parents fbea2db + da5a622 commit de209cf
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 40 deletions.
9 changes: 0 additions & 9 deletions src/libs/Trie/TrieNode.js

This file was deleted.

19 changes: 19 additions & 0 deletions src/libs/Trie/TrieNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
type MetaData = Record<string, unknown>;

class TrieNode<TMetadata extends MetaData> {
children: Record<string, TrieNode<TMetadata>>;

metaData: Partial<TMetadata>;

isEndOfWord: boolean;

constructor() {
this.children = {};
this.metaData = {};
this.isEndOfWord = false;
}
}

export default TrieNode;

export type {MetaData};
56 changes: 25 additions & 31 deletions src/libs/Trie/index.js → src/libs/Trie/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import _ from 'underscore';
import TrieNode from './TrieNode';
import TrieNode, {MetaData} from './TrieNode';

type Word<TMetaData extends MetaData> = {
name: string;
metaData: Partial<TMetaData>;
};

class Trie<TMetaData extends MetaData> {
root: TrieNode<TMetaData>;

class Trie {
constructor() {
this.root = new TrieNode();
}

/**
* Add a word to the Trie
* @param {String} word
* @param {Object} [metaData] - attach additional data to the word
* @param {TrieNode} [node]
* @param {Boolean} [allowEmptyWords] - empty word doesn't have any char, you shouldn't pass a true value for it because we are disallowing adding an empty word
* @param [metaData] - attach additional data to the word
* @param [allowEmptyWords] - empty word doesn't have any char, you shouldn't pass a true value for it because we are disallowing adding an empty word
*/
add(word, metaData = {}, node = this.root, allowEmptyWords = false) {
add(word: string, metaData: Partial<TMetaData> = {}, node = this.root, allowEmptyWords = false) {
const newWord = word.toLowerCase();
const newNode = node;
if (newWord.length === 0 && !allowEmptyWords) {
Expand All @@ -33,10 +37,9 @@ class Trie {

/**
* Search for a word in the Trie.
* @param {String} word
* @returns {Object|null} – the node for the word if it's found, or null if it's not found
* @returns - the node for the word if it's found, or null if it's not found
*/
search(word) {
search(word: string): TrieNode<TMetaData> | null {
let newWord = word.toLowerCase();
let node = this.root;
while (newWord.length > 1) {
Expand All @@ -52,10 +55,8 @@ class Trie {

/**
* Update a word data in the Trie.
* @param {String} word
* @param {Object} metaData
*/
update(word, metaData) {
update(word: string, metaData: TMetaData) {
let newWord = word.toLowerCase();
let node = this.root;
while (newWord.length > 1) {
Expand All @@ -70,43 +71,36 @@ class Trie {

/**
* Find all leaf nodes starting with a substring.
* @param {String} substr
* @param {Number} [limit] - matching words limit
* @returns {Array}
* @param [limit] - matching words limit
*/
getAllMatchingWords(substr, limit = 5) {
getAllMatchingWords(substr: string, limit = 5): Array<Word<TMetaData>> {
const newSubstr = substr.toLowerCase();
let node = this.root;
let prefix = '';
for (let i = 0; i < newSubstr.length; i++) {
prefix += newSubstr[i];
if (!node.children[newSubstr[i]]) {
for (const char of newSubstr) {
prefix += char;
if (!node.children[char]) {
return [];
}
node = node.children[newSubstr[i]];
node = node.children[char];
}
return this.getChildMatching(node, prefix, limit, []);
}

/**
* Find all leaf nodes that are descendants of a given child node.
* @param {TrieNode} node
* @param {String} prefix
* @param {Number} limit
* @param {Array} [words]
* @returns {Array}
*/
getChildMatching(node, prefix, limit, words = []) {
getChildMatching(node: TrieNode<TMetaData>, prefix: string, limit: number, words: Array<Word<TMetaData>> = []): Array<Word<TMetaData>> {
const matching = words;
if (matching.length >= limit) {
return matching;
}
if (node.isEndOfWord) {
matching.push({name: prefix, metaData: node.metaData});
}
const children = _.keys(node.children);
for (let i = 0; i < children.length; i++) {
this.getChildMatching(node.children[children[i]], prefix + children[i], limit, matching);
const children = Object.keys(node.children);
for (const child of children) {
this.getChildMatching(node.children[child], prefix + child, limit, matching);
}
return matching;
}
Expand Down

0 comments on commit de209cf

Please sign in to comment.