mirror of
https://github.com/zrwusa/data-structure-typed.git
synced 2024-11-23 12:54:04 +00:00
Compare commits
No commits in common. "ca7e60e465dd784db46cb289fbee557512bbea32" and "c3122e7fc7866955db944f776382315e76615b29" have entirely different histories.
ca7e60e465
...
c3122e7fc7
|
@ -8,7 +8,7 @@ All notable changes to this project will be documented in this file.
|
|||
- [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
||||
- [`auto-changelog`](https://github.com/CookPete/auto-changelog)
|
||||
|
||||
## [v1.53.7](https://github.com/zrwusa/data-structure-typed/compare/v1.51.5...main) (upcoming)
|
||||
## [v1.53.6](https://github.com/zrwusa/data-structure-typed/compare/v1.51.5...main) (upcoming)
|
||||
|
||||
### Changes
|
||||
|
||||
|
|
52
package-lock.json
generated
52
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "data-structure-typed",
|
||||
"version": "1.53.7",
|
||||
"version": "1.53.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "data-structure-typed",
|
||||
"version": "1.53.7",
|
||||
"version": "1.53.6",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^1.2.2",
|
||||
|
@ -19,11 +19,11 @@
|
|||
"@typescript-eslint/eslint-plugin": "^8.12.1",
|
||||
"@typescript-eslint/parser": "^8.12.1",
|
||||
"auto-changelog": "^2.5.0",
|
||||
"avl-tree-typed": "^1.53.6",
|
||||
"avl-tree-typed": "^1.53.5",
|
||||
"benchmark": "^2.1.4",
|
||||
"binary-tree-typed": "^1.53.6",
|
||||
"bst-typed": "^1.53.6",
|
||||
"data-structure-typed": "^1.53.6",
|
||||
"binary-tree-typed": "^1.53.5",
|
||||
"bst-typed": "^1.53.5",
|
||||
"data-structure-typed": "^1.53.5",
|
||||
"dependency-cruiser": "^16.5.0",
|
||||
"doctoc": "^2.2.1",
|
||||
"eslint": "^9.13.0",
|
||||
|
@ -32,7 +32,7 @@
|
|||
"eslint-import-resolver-typescript": "^3.6.3",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"heap-typed": "^1.53.6",
|
||||
"heap-typed": "^1.53.5",
|
||||
"istanbul-badges-readme": "^1.9.0",
|
||||
"jest": "^29.7.0",
|
||||
"js-sdsl": "^4.4.2",
|
||||
|
@ -3437,13 +3437,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/avl-tree-typed": {
|
||||
"version": "1.53.6",
|
||||
"resolved": "https://registry.npmjs.org/avl-tree-typed/-/avl-tree-typed-1.53.6.tgz",
|
||||
"integrity": "sha512-BmWYQG+mvfoS9yPVAm3CRL3omYdCO1Rohe7BGkYjvC1WHhd0A6FIrwu5Ge9kyrPxxqQsZWK/D4YHDqTyWkX4+A==",
|
||||
"version": "1.53.5",
|
||||
"resolved": "https://registry.npmjs.org/avl-tree-typed/-/avl-tree-typed-1.53.5.tgz",
|
||||
"integrity": "sha512-BkJgD5S4ALEl4ytKKVvqz3Qo/19U2bxnOUyZxy3dMgXNh20vaPZAJ82bFRX2lAoBFBDmIgtWp/klxC0CrDO6nQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"data-structure-typed": "^1.53.6"
|
||||
"data-structure-typed": "^1.53.5"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-jest": {
|
||||
|
@ -3602,13 +3602,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/binary-tree-typed": {
|
||||
"version": "1.53.6",
|
||||
"resolved": "https://registry.npmjs.org/binary-tree-typed/-/binary-tree-typed-1.53.6.tgz",
|
||||
"integrity": "sha512-cLjhoqLjoY+iMbMcvQsjT1jhCHkdGI4j3cEdLG4g3Vy99jipxiXQ3h9qSmWYo5HO7w5DBfU4v+xw4yI5lM3+AQ==",
|
||||
"version": "1.53.5",
|
||||
"resolved": "https://registry.npmjs.org/binary-tree-typed/-/binary-tree-typed-1.53.5.tgz",
|
||||
"integrity": "sha512-4DSrFAlykAMRF2w/hqaPG+vzsQLKq8TTnVBTD2c9Ew680+jHcanypDb6mzW0WJWSgA+v5V9IEX7P6Zz6/vS1rg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"data-structure-typed": "^1.53.6"
|
||||
"data-structure-typed": "^1.53.5"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
|
@ -3691,13 +3691,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/bst-typed": {
|
||||
"version": "1.53.6",
|
||||
"resolved": "https://registry.npmjs.org/bst-typed/-/bst-typed-1.53.6.tgz",
|
||||
"integrity": "sha512-LKY9GMNdhYlb8Iprc8Z4143bQ6cWYhE8thSHb8jhvVzsEuqXqetM0tSG88a5QjURgxt+Mr9GGJruSeBYX7/vQw==",
|
||||
"version": "1.53.5",
|
||||
"resolved": "https://registry.npmjs.org/bst-typed/-/bst-typed-1.53.5.tgz",
|
||||
"integrity": "sha512-poYNwY4rwHinpNCQDsTv631XGUNJh/wvklLzaok+Nt+XV1PsH2e8VATaTCUMdTwLFrDn7jGFoY8mJkJjmr0s2w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"data-structure-typed": "^1.53.6"
|
||||
"data-structure-typed": "^1.53.5"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
|
@ -4069,9 +4069,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/data-structure-typed": {
|
||||
"version": "1.53.6",
|
||||
"resolved": "https://registry.npmjs.org/data-structure-typed/-/data-structure-typed-1.53.6.tgz",
|
||||
"integrity": "sha512-o3j49Yb5nPWyMIYvm6KOqlaj6SbUP/nq26kl6KmT0UknbOPfrElGIAdYwV9J87dHkHxGPGJG7Ee754jv8XjIIQ==",
|
||||
"version": "1.53.5",
|
||||
"resolved": "https://registry.npmjs.org/data-structure-typed/-/data-structure-typed-1.53.5.tgz",
|
||||
"integrity": "sha512-v7gFtZe71/vS/+HR+eK3EZmljvp5ANJK3DiCjdLC5XGmzpLvJVwfYz+An9f8+FEoM6DXrKLAdDkZPU9m/hZW7g==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
@ -5946,13 +5946,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/heap-typed": {
|
||||
"version": "1.53.6",
|
||||
"resolved": "https://registry.npmjs.org/heap-typed/-/heap-typed-1.53.6.tgz",
|
||||
"integrity": "sha512-MS8mjgg/rjV6wQmD/sqdr93M0WGezju/Q4KFULvF+JpD7XVmdpfN/w+h9fA4ZYkkI5Ih7JarJvvh8hHHWfpqIg==",
|
||||
"version": "1.53.5",
|
||||
"resolved": "https://registry.npmjs.org/heap-typed/-/heap-typed-1.53.5.tgz",
|
||||
"integrity": "sha512-H5Vj0U+fXyvdyW5rSZ44kOI7WeThzaNihhwD7kQ+SkTjM9+sskUvx6JpZf62ca56NRt+57w42gg6A/A3S8W0bA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"data-structure-typed": "^1.53.6"
|
||||
"data-structure-typed": "^1.53.5"
|
||||
}
|
||||
},
|
||||
"node_modules/html-escaper": {
|
||||
|
|
10
package.json
10
package.json
|
@ -70,11 +70,11 @@
|
|||
"@typescript-eslint/eslint-plugin": "^8.12.1",
|
||||
"@typescript-eslint/parser": "^8.12.1",
|
||||
"auto-changelog": "^2.5.0",
|
||||
"avl-tree-typed": "^1.53.6",
|
||||
"avl-tree-typed": "^1.53.5",
|
||||
"benchmark": "^2.1.4",
|
||||
"binary-tree-typed": "^1.53.6",
|
||||
"bst-typed": "^1.53.6",
|
||||
"data-structure-typed": "^1.53.6",
|
||||
"binary-tree-typed": "^1.53.5",
|
||||
"bst-typed": "^1.53.5",
|
||||
"data-structure-typed": "^1.53.5",
|
||||
"dependency-cruiser": "^16.5.0",
|
||||
"doctoc": "^2.2.1",
|
||||
"eslint": "^9.13.0",
|
||||
|
@ -83,7 +83,7 @@
|
|||
"eslint-import-resolver-typescript": "^3.6.3",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"fast-glob": "^3.3.2",
|
||||
"heap-typed": "^1.53.6",
|
||||
"heap-typed": "^1.53.5",
|
||||
"istanbul-badges-readme": "^1.9.0",
|
||||
"jest": "^29.7.0",
|
||||
"js-sdsl": "^4.4.2",
|
||||
|
|
|
@ -254,14 +254,7 @@ function updateExamples(testDir: string, sourceBaseDir: string): void {
|
|||
* Replace content between markers in README.md.
|
||||
*/
|
||||
function replaceExamplesInReadme(readmePath: string, newExamples: string[]): void {
|
||||
let readmeContent: string;
|
||||
|
||||
try {
|
||||
readmeContent = fs.readFileSync(readmePath, 'utf-8');
|
||||
} catch (error) {
|
||||
console.warn(`Failed to read ${fileName} at ${readmePath}: ${error}`);
|
||||
return;
|
||||
}
|
||||
const readmeContent = fs.readFileSync(readmePath, 'utf-8');
|
||||
|
||||
const startIdx = readmeContent.indexOf(START_MARKER);
|
||||
const endIdx = readmeContent.indexOf(END_MARKER);
|
||||
|
@ -276,7 +269,7 @@ function replaceExamplesInReadme(readmePath: string, newExamples: string[]): voi
|
|||
const updatedContent = `${before}\n\n${newExamples.join('\n\n')}\n\n${after}`;
|
||||
fs.writeFileSync(readmePath, updatedContent, 'utf-8');
|
||||
|
||||
console.log(`${fileName} updated with new examples.`);
|
||||
console.log(`README.md updated with new examples.`);
|
||||
}
|
||||
|
||||
// Run the script
|
||||
|
|
|
@ -173,7 +173,7 @@ export class AVLTreeMultiMap<
|
|||
* times the key-value pair should be added to the data structure. If not provided, it defaults to 1.
|
||||
* @returns either a NODE object or undefined.
|
||||
*/
|
||||
protected override _keyValueNodeEntryRawToNodeAndValue(
|
||||
override keyValueNodeEntryRawToNodeAndValue(
|
||||
keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R,
|
||||
value?: V,
|
||||
count = 1
|
||||
|
@ -217,7 +217,7 @@ export class AVLTreeMultiMap<
|
|||
* @returns a boolean value.
|
||||
*/
|
||||
override add(keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R, value?: V, count = 1): boolean {
|
||||
const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value, count);
|
||||
const [newNode, newValue] = this.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value, count);
|
||||
if (newNode === undefined) return false;
|
||||
|
||||
const orgNodeCount = newNode?.count || 0;
|
||||
|
|
|
@ -216,7 +216,7 @@ export class BinaryTree<
|
|||
* input parameter (`keyNodeEntryOrRaw`) and processes it accordingly to return a node or null
|
||||
* value.
|
||||
*/
|
||||
protected _keyValueNodeEntryRawToNodeAndValue(
|
||||
keyValueNodeEntryRawToNodeAndValue(
|
||||
keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R,
|
||||
value?: V
|
||||
): [OptNodeOrNull<NODE>, V | undefined] {
|
||||
|
@ -420,7 +420,7 @@ export class BinaryTree<
|
|||
* key was found and the node was replaced instead of inserted.
|
||||
*/
|
||||
add(keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R, value?: V): boolean {
|
||||
const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
|
||||
const [newNode, newValue] = this.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
|
||||
if (newNode === undefined) return false;
|
||||
|
||||
// If the tree is empty, directly set the new node as the root node
|
||||
|
|
|
@ -112,11 +112,15 @@ export class BSTNode<K = any, V = any, NODE extends BSTNode<K, V, NODE> = BSTNod
|
|||
* @example
|
||||
* // Find elements in a range
|
||||
* const bst = new BST<number>([10, 5, 15, 3, 7, 12, 18]);
|
||||
* console.log(bst.search(new Range(5, 10))); // [10, 5, 7]
|
||||
* console.log(bst.search(new Range(4, 12))); // [10, 12, 5, 7]
|
||||
* console.log(bst.search(new Range(4, 12, true, false))); // [10, 5, 7]
|
||||
* console.log(bst.search(new Range(15, 20))); // [15, 18]
|
||||
* console.log(bst.search(new Range(15, 20, false))); // [18]
|
||||
*
|
||||
* // Helper function to find elements in range
|
||||
* const findElementsInRange = (min: number, max: number): number[] => {
|
||||
* return bst.search(node => node.key >= min && node.key <= max, false, node => node.key);
|
||||
* };
|
||||
*
|
||||
* // Assertions
|
||||
* console.log(findElementsInRange(4, 12)); // [10, 5, 7, 12]
|
||||
* console.log(findElementsInRange(15, 20)); // [15, 18]
|
||||
* @example
|
||||
* // Find lowest common ancestor
|
||||
* const bst = new BST<number>([20, 10, 30, 5, 15, 25, 35, 3, 7, 12, 18]);
|
||||
|
@ -233,11 +237,11 @@ export class BST<
|
|||
* value associated with a key in a key-value pair.
|
||||
* @returns either a NODE object or undefined.
|
||||
*/
|
||||
protected override _keyValueNodeEntryRawToNodeAndValue(
|
||||
override keyValueNodeEntryRawToNodeAndValue(
|
||||
keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R,
|
||||
value?: V
|
||||
): [OptNode<NODE>, V | undefined] {
|
||||
const [node, entryValue] = super._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
|
||||
const [node, entryValue] = super.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
|
||||
if (node === null) return [undefined, undefined];
|
||||
return [node, value ?? entryValue];
|
||||
}
|
||||
|
@ -299,7 +303,7 @@ export class BST<
|
|||
* @returns a boolean value.
|
||||
*/
|
||||
override add(keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R, value?: V): boolean {
|
||||
const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
|
||||
const [newNode, newValue] = this.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
|
||||
if (newNode === undefined) return false;
|
||||
|
||||
if (this._root === undefined) {
|
||||
|
|
|
@ -207,7 +207,7 @@ export class RedBlackTree<
|
|||
* returns true. If the node cannot be added or updated, the method returns false.
|
||||
*/
|
||||
override add(keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R, value?: V): boolean {
|
||||
const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
|
||||
const [newNode, newValue] = this.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value);
|
||||
if (!this.isRealNode(newNode)) return false;
|
||||
|
||||
const insertStatus = this._insert(newNode);
|
||||
|
|
|
@ -157,7 +157,7 @@ export class TreeMultiMap<
|
|||
* times the key-value pair should be added to the data structure. If not provided, it defaults to 1.
|
||||
* @returns either a NODE object or undefined.
|
||||
*/
|
||||
protected override _keyValueNodeEntryRawToNodeAndValue(
|
||||
override keyValueNodeEntryRawToNodeAndValue(
|
||||
keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R,
|
||||
value?: V,
|
||||
count = 1
|
||||
|
@ -212,7 +212,7 @@ export class TreeMultiMap<
|
|||
* was successful, and false otherwise.
|
||||
*/
|
||||
override add(keyNodeEntryOrRaw: BTNRep<K, V, NODE> | R, value?: V, count = 1): boolean {
|
||||
const [newNode, newValue] = this._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value, count);
|
||||
const [newNode, newValue] = this.keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value, count);
|
||||
const orgCount = newNode?.count || 0;
|
||||
const isSuccessAdded = super.add(newNode, newValue);
|
||||
|
||||
|
|
|
@ -514,16 +514,6 @@ export class DoublyLinkedListNode<E = any> {
|
|||
* console.log(scheduler.listProcesses()); // []
|
||||
*/
|
||||
export class DoublyLinkedList<E = any, R = any> extends IterableElementBase<E, R, DoublyLinkedList<E, R>> {
|
||||
/**
|
||||
* This TypeScript constructor initializes a DoublyLinkedList with optional elements and options.
|
||||
* @param {Iterable<E> | Iterable<R>} elements - The `elements` parameter in the constructor is an
|
||||
* iterable collection of elements of type `E` or `R`. It is used to initialize the DoublyLinkedList
|
||||
* with the elements provided in the iterable. If no elements are provided, the default value is an
|
||||
* empty iterable.
|
||||
* @param [options] - The `options` parameter in the constructor is of type
|
||||
* `DoublyLinkedListOptions<E, R>`. It is an optional parameter that allows you to pass additional
|
||||
* configuration options to customize the behavior of the DoublyLinkedList.
|
||||
*/
|
||||
constructor(elements: Iterable<E> | Iterable<R> = [], options?: DoublyLinkedListOptions<E, R>) {
|
||||
super(options);
|
||||
this._head = undefined;
|
||||
|
|
|
@ -92,100 +92,13 @@ export class TrieNode {
|
|||
* 9. Spell Check: Checking the spelling of words.
|
||||
* 10. IP Routing: Used in certain types of IP routing algorithms.
|
||||
* 11. Text Word Frequency Count: Counting and storing the frequency of words in a large amount of text data.
|
||||
* @example
|
||||
* // Autocomplete: Prefix validation and checking
|
||||
* const autocomplete = new Trie<string>(['gmail.com', 'gmail.co.nz', 'gmail.co.jp', 'yahoo.com', 'outlook.com']);
|
||||
*
|
||||
* // Get all completions for a prefix
|
||||
* const gmailCompletions = autocomplete.getWords('gmail');
|
||||
* console.log(gmailCompletions); // ['gmail.com', 'gmail.co.nz', 'gmail.co.jp']
|
||||
* @example
|
||||
* // File System Path Operations
|
||||
* const fileSystem = new Trie<string>([
|
||||
* '/home/user/documents/file1.txt',
|
||||
* '/home/user/documents/file2.txt',
|
||||
* '/home/user/pictures/photo.jpg',
|
||||
* '/home/user/pictures/vacation/',
|
||||
* '/home/user/downloads'
|
||||
* ]);
|
||||
*
|
||||
* // Find common directory prefix
|
||||
* console.log(fileSystem.getLongestCommonPrefix()); // '/home/user/'
|
||||
*
|
||||
* // List all files in a directory
|
||||
* const documentsFiles = fileSystem.getWords('/home/user/documents/');
|
||||
* console.log(documentsFiles); // ['/home/user/documents/file1.txt', '/home/user/documents/file2.txt']
|
||||
* @example
|
||||
* // Autocomplete: Basic word suggestions
|
||||
* // Create a trie for autocomplete
|
||||
* const autocomplete = new Trie<string>([
|
||||
* 'function',
|
||||
* 'functional',
|
||||
* 'functions',
|
||||
* 'class',
|
||||
* 'classes',
|
||||
* 'classical',
|
||||
* 'closure',
|
||||
* 'const',
|
||||
* 'constructor'
|
||||
* ]);
|
||||
*
|
||||
* // Test autocomplete with different prefixes
|
||||
* console.log(autocomplete.getWords('fun')); // ['functional', 'functions', 'function']
|
||||
* console.log(autocomplete.getWords('cla')); // ['classes', 'classical', 'class']
|
||||
* console.log(autocomplete.getWords('con')); // ['constructor', 'const']
|
||||
*
|
||||
* // Test with non-matching prefix
|
||||
* console.log(autocomplete.getWords('xyz')); // []
|
||||
* @example
|
||||
* // Dictionary: Case-insensitive word lookup
|
||||
* // Create a case-insensitive dictionary
|
||||
* const dictionary = new Trie<string>([], { caseSensitive: false });
|
||||
*
|
||||
* // Add words with mixed casing
|
||||
* dictionary.add('Hello');
|
||||
* dictionary.add('WORLD');
|
||||
* dictionary.add('JavaScript');
|
||||
*
|
||||
* // Test lookups with different casings
|
||||
* console.log(dictionary.has('hello')); // true
|
||||
* console.log(dictionary.has('HELLO')); // true
|
||||
* console.log(dictionary.has('Hello')); // true
|
||||
* console.log(dictionary.has('javascript')); // true
|
||||
* console.log(dictionary.has('JAVASCRIPT')); // true
|
||||
* @example
|
||||
* // IP Address Routing Table
|
||||
* // Add IP address prefixes and their corresponding routes
|
||||
* const routes = {
|
||||
* '192.168.1': 'LAN_SUBNET_1',
|
||||
* '192.168.2': 'LAN_SUBNET_2',
|
||||
* '10.0.0': 'PRIVATE_NETWORK_1',
|
||||
* '10.0.1': 'PRIVATE_NETWORK_2'
|
||||
* };
|
||||
*
|
||||
* const ipRoutingTable = new Trie<string>(Object.keys(routes));
|
||||
*
|
||||
* // Check IP address prefix matching
|
||||
* console.log(ipRoutingTable.hasPrefix('192.168.1')); // true
|
||||
* console.log(ipRoutingTable.hasPrefix('192.168.2')); // true
|
||||
*
|
||||
* // Validate IP address belongs to subnet
|
||||
* const ip = '192.168.1.100';
|
||||
* const subnet = ip.split('.').slice(0, 3).join('.');
|
||||
* console.log(ipRoutingTable.hasPrefix(subnet)); // true
|
||||
*/
|
||||
export class Trie<R = any> extends IterableElementBase<string, R, Trie<R>> {
|
||||
/**
|
||||
* The constructor initializes a Trie data structure with optional options and words provided as
|
||||
* input.
|
||||
* @param {Iterable<string> | Iterable<R>} words - The `words` parameter in the constructor is an
|
||||
* iterable containing either strings or elements of type `R`. It is used to initialize the Trie with
|
||||
* a list of words or elements. If no `words` are provided, an empty iterable is used as the default
|
||||
* value.
|
||||
* @param [options] - The `options` parameter in the constructor is an optional object that can
|
||||
* contain configuration options for the Trie data structure. One of the options it can have is
|
||||
* `caseSensitive`, which is a boolean value indicating whether the Trie should be case-sensitive or
|
||||
* not. If `caseSensitive` is set to `
|
||||
* The constructor function for the Trie class.
|
||||
* @param words: Iterable string Initialize the trie with a set of words
|
||||
* @param options?: TrieOptions Allow the user to pass in options for the trie
|
||||
* @return This
|
||||
*/
|
||||
constructor(words: Iterable<string> | Iterable<R> = [], options?: TrieOptions<R>) {
|
||||
super(options);
|
||||
|
@ -194,7 +107,13 @@ export class Trie<R = any> extends IterableElementBase<string, R, Trie<R>> {
|
|||
if (caseSensitive !== undefined) this._caseSensitive = caseSensitive;
|
||||
}
|
||||
if (words) {
|
||||
this.addMany(words);
|
||||
for (const word of words) {
|
||||
if (this.toElementFn) {
|
||||
this.add(this.toElementFn(word as R));
|
||||
} else {
|
||||
this.add(word as string);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,30 +175,6 @@ export class Trie<R = any> extends IterableElementBase<string, R, Trie<R>> {
|
|||
return isNewWord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(n * l)
|
||||
* Space Complexity: O(1)
|
||||
*
|
||||
* The `addMany` function in TypeScript takes an iterable of strings or elements of type R, converts
|
||||
* them using a provided function if available, and adds them to a data structure while returning an
|
||||
* array of boolean values indicating success.
|
||||
* @param {Iterable<string> | Iterable<R>} words - The `words` parameter in the `addMany` function is
|
||||
* an iterable that contains either strings or elements of type `R`.
|
||||
* @returns The `addMany` method returns an array of boolean values indicating whether each word in
|
||||
* the input iterable was successfully added to the data structure.
|
||||
*/
|
||||
addMany(words: Iterable<string> | Iterable<R> = []): boolean[] {
|
||||
const ans: boolean[] = [];
|
||||
for (const word of words) {
|
||||
if (this.toElementFn) {
|
||||
ans.push(this.add(this.toElementFn(word as R)));
|
||||
} else {
|
||||
ans.push(this.add(word as string));
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time Complexity: O(l), where l is the length of the input word.
|
||||
* Space Complexity: O(1) - Constant space.
|
||||
|
|
|
@ -733,41 +733,41 @@ describe('BinaryTree', () => {
|
|||
]);
|
||||
});
|
||||
|
||||
// it('should keyValueNodeEntryRawToNodeAndValue', () => {
|
||||
// const tree = new BinaryTree<number>();
|
||||
// const node0 = tree.keyValueNodeEntryRawToNodeAndValue(0);
|
||||
// expect(node0).toEqual([
|
||||
// {
|
||||
// _left: undefined,
|
||||
// _right: undefined,
|
||||
// key: 0,
|
||||
// parent: undefined,
|
||||
// value: undefined
|
||||
// },
|
||||
// undefined
|
||||
// ]);
|
||||
//
|
||||
// const nodeUndefined = tree.keyValueNodeEntryRawToNodeAndValue(undefined);
|
||||
// expect(nodeUndefined).toEqual([undefined, undefined]);
|
||||
//
|
||||
// const nodeNull = tree.keyValueNodeEntryRawToNodeAndValue(null);
|
||||
// expect(nodeNull).toEqual([null, undefined]);
|
||||
//
|
||||
// const [, nodeWithSeparateValue] = tree.keyValueNodeEntryRawToNodeAndValue(7, 77);
|
||||
// expect(nodeWithSeparateValue).toBe(77);
|
||||
//
|
||||
// expect(tree.keyValueNodeEntryRawToNodeAndValue([undefined, 2])).toEqual([undefined, undefined]);
|
||||
//
|
||||
// expect(tree.keyValueNodeEntryRawToNodeAndValue(Symbol('test') as unknown as number)).toEqual([
|
||||
// undefined,
|
||||
// undefined
|
||||
// ]);
|
||||
//
|
||||
// const bTree = new BinaryTree<number, number, { obj: { id: number } }>([], {
|
||||
// toEntryFn: (ele: { obj: { id: number } }) => [Symbol('test') as unknown as number, ele.obj.id]
|
||||
// });
|
||||
// expect(bTree.keyValueNodeEntryRawToNodeAndValue({ obj: { id: 1 } })).toEqual([undefined, undefined]);
|
||||
// });
|
||||
it('should keyValueNodeEntryRawToNodeAndValue', () => {
|
||||
const tree = new BinaryTree<number>();
|
||||
const node0 = tree.keyValueNodeEntryRawToNodeAndValue(0);
|
||||
expect(node0).toEqual([
|
||||
{
|
||||
_left: undefined,
|
||||
_right: undefined,
|
||||
key: 0,
|
||||
parent: undefined,
|
||||
value: undefined
|
||||
},
|
||||
undefined
|
||||
]);
|
||||
|
||||
const nodeUndefined = tree.keyValueNodeEntryRawToNodeAndValue(undefined);
|
||||
expect(nodeUndefined).toEqual([undefined, undefined]);
|
||||
|
||||
const nodeNull = tree.keyValueNodeEntryRawToNodeAndValue(null);
|
||||
expect(nodeNull).toEqual([null, undefined]);
|
||||
|
||||
const [, nodeWithSeparateValue] = tree.keyValueNodeEntryRawToNodeAndValue(7, 77);
|
||||
expect(nodeWithSeparateValue).toBe(77);
|
||||
|
||||
expect(tree.keyValueNodeEntryRawToNodeAndValue([undefined, 2])).toEqual([undefined, undefined]);
|
||||
|
||||
expect(tree.keyValueNodeEntryRawToNodeAndValue(Symbol('test') as unknown as number)).toEqual([
|
||||
undefined,
|
||||
undefined
|
||||
]);
|
||||
|
||||
const bTree = new BinaryTree<number, number, { obj: { id: number } }>([], {
|
||||
toEntryFn: (ele: { obj: { id: number } }) => [Symbol('test') as unknown as number, ele.obj.id]
|
||||
});
|
||||
expect(bTree.keyValueNodeEntryRawToNodeAndValue({ obj: { id: 1 } })).toEqual([undefined, undefined]);
|
||||
});
|
||||
|
||||
it('should replace value', () => {
|
||||
const tree = new BinaryTree<number, string>([4, 5, [1, '1'], 2, 3], { isMapMode: false });
|
||||
|
|
|
@ -450,26 +450,26 @@ describe('BST operations test', () => {
|
|||
expect(bfsNodes[2].key).toBe(16);
|
||||
});
|
||||
|
||||
// it('should keyValueNodeEntryRawToNodeAndValue', () => {
|
||||
// const bst = new BST<number>();
|
||||
// const node0 = bst.keyValueNodeEntryRawToNodeAndValue(0);
|
||||
// expect(node0).toEqual([
|
||||
// {
|
||||
// _left: undefined,
|
||||
// _right: undefined,
|
||||
// key: 0,
|
||||
// parent: undefined,
|
||||
// value: undefined
|
||||
// },
|
||||
// undefined
|
||||
// ]);
|
||||
//
|
||||
// const nodeUndefined = bst.keyValueNodeEntryRawToNodeAndValue(undefined);
|
||||
// expect(nodeUndefined).toEqual([undefined, undefined]);
|
||||
//
|
||||
// const nodeNull = bst.keyValueNodeEntryRawToNodeAndValue(null);
|
||||
// expect(nodeNull).toEqual([undefined, undefined]);
|
||||
// });
|
||||
it('should keyValueNodeEntryRawToNodeAndValue', () => {
|
||||
const bst = new BST<number>();
|
||||
const node0 = bst.keyValueNodeEntryRawToNodeAndValue(0);
|
||||
expect(node0).toEqual([
|
||||
{
|
||||
_left: undefined,
|
||||
_right: undefined,
|
||||
key: 0,
|
||||
parent: undefined,
|
||||
value: undefined
|
||||
},
|
||||
undefined
|
||||
]);
|
||||
|
||||
const nodeUndefined = bst.keyValueNodeEntryRawToNodeAndValue(undefined);
|
||||
expect(nodeUndefined).toEqual([undefined, undefined]);
|
||||
|
||||
const nodeNull = bst.keyValueNodeEntryRawToNodeAndValue(null);
|
||||
expect(nodeNull).toEqual([undefined, undefined]);
|
||||
});
|
||||
|
||||
it('should replace value', () => {
|
||||
const tree = new BST<number, string>([4, 5, [1, '1'], 2, 3], { isMapMode: false });
|
||||
|
@ -1340,26 +1340,26 @@ describe('BST operations not map mode test', () => {
|
|||
});
|
||||
});
|
||||
|
||||
// it('should keyValueNodeEntryRawToNodeAndValue', () => {
|
||||
// const bst = new BST<number>([], { isMapMode: false });
|
||||
// const node0 = bst.keyValueNodeEntryRawToNodeAndValue(0);
|
||||
// expect(node0).toEqual([
|
||||
// {
|
||||
// _left: undefined,
|
||||
// _right: undefined,
|
||||
// key: 0,
|
||||
// parent: undefined,
|
||||
// value: undefined
|
||||
// },
|
||||
// undefined
|
||||
// ]);
|
||||
//
|
||||
// const nodeUndefined = bst.keyValueNodeEntryRawToNodeAndValue(undefined);
|
||||
// expect(nodeUndefined).toEqual([undefined, undefined]);
|
||||
//
|
||||
// const nodeNull = bst.keyValueNodeEntryRawToNodeAndValue(null);
|
||||
// expect(nodeNull).toEqual([undefined, undefined]);
|
||||
// });
|
||||
it('should keyValueNodeEntryRawToNodeAndValue', () => {
|
||||
const bst = new BST<number>([], { isMapMode: false });
|
||||
const node0 = bst.keyValueNodeEntryRawToNodeAndValue(0);
|
||||
expect(node0).toEqual([
|
||||
{
|
||||
_left: undefined,
|
||||
_right: undefined,
|
||||
key: 0,
|
||||
parent: undefined,
|
||||
value: undefined
|
||||
},
|
||||
undefined
|
||||
]);
|
||||
|
||||
const nodeUndefined = bst.keyValueNodeEntryRawToNodeAndValue(undefined);
|
||||
expect(nodeUndefined).toEqual([undefined, undefined]);
|
||||
|
||||
const nodeNull = bst.keyValueNodeEntryRawToNodeAndValue(null);
|
||||
expect(nodeNull).toEqual([undefined, undefined]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('BST operations not map mode test recursively', () => {
|
||||
|
|
|
@ -932,154 +932,3 @@ describe('Trie class', () => {
|
|||
expect(trieB.hasPrefix('ap')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Trie basic', () => {
|
||||
test('Dictionary: Basic word lookup functionality', () => {
|
||||
// Initialize a new Trie and add dictionary words
|
||||
const dictionary = new Trie<string>();
|
||||
const words = ['apple', 'app', 'application', 'approve', 'bread', 'break'];
|
||||
words.forEach(word => dictionary.add(word));
|
||||
|
||||
// Test exact word matches
|
||||
expect(dictionary.has('apple')).toBe(true);
|
||||
expect(dictionary.has('app')).toBe(true);
|
||||
expect(dictionary.has('bread')).toBe(true);
|
||||
|
||||
// Test non-existent words
|
||||
expect(dictionary.has('appl')).toBe(false);
|
||||
expect(dictionary.has('breaking')).toBe(false);
|
||||
|
||||
// Verify dictionary size
|
||||
expect(dictionary.size).toBe(words.length);
|
||||
});
|
||||
|
||||
test('Autocomplete: Limited suggestions with max results', () => {
|
||||
const autocomplete = new Trie<string>();
|
||||
|
||||
// Add city names
|
||||
const cities = ['New York', 'New Orleans', 'New Delhi', 'New Jersey', 'New Mexico', 'New Hampshire'];
|
||||
|
||||
cities.forEach(city => autocomplete.add(city));
|
||||
|
||||
// Get limited number of suggestions
|
||||
const maxSuggestions = 3;
|
||||
const suggestions = autocomplete.getWords('New', maxSuggestions);
|
||||
|
||||
expect(suggestions.length).toBe(maxSuggestions);
|
||||
suggestions.forEach(suggestion => {
|
||||
expect(suggestion.startsWith('New')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('Dictionary: Word removal and updates', () => {
|
||||
const dictionary = new Trie<string>();
|
||||
|
||||
// Add initial words
|
||||
dictionary.add('delete');
|
||||
dictionary.add('deletion');
|
||||
dictionary.add('deleted');
|
||||
|
||||
// Verify initial state
|
||||
expect(dictionary.has('delete')).toBe(true);
|
||||
expect(dictionary.size).toBe(3);
|
||||
|
||||
// Remove a word
|
||||
const deleted = dictionary.delete('delete');
|
||||
expect(deleted).toBe(true);
|
||||
expect(dictionary.has('delete')).toBe(false);
|
||||
expect(dictionary.has('deletion')).toBe(true);
|
||||
expect(dictionary.has('deleted')).toBe(true);
|
||||
expect(dictionary.size).toBe(2);
|
||||
|
||||
// Try to remove non-existent word
|
||||
expect(dictionary.delete('notexist')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('classic use', () => {
|
||||
test('@example Autocomplete: Prefix validation and checking', () => {
|
||||
const autocomplete = new Trie<string>(['gmail.com', 'gmail.co.nz', 'gmail.co.jp', 'yahoo.com', 'outlook.com']);
|
||||
|
||||
// Get all completions for a prefix
|
||||
const gmailCompletions = autocomplete.getWords('gmail');
|
||||
expect(gmailCompletions).toEqual(['gmail.com', 'gmail.co.nz', 'gmail.co.jp']);
|
||||
});
|
||||
|
||||
test('@example File System Path Operations', () => {
|
||||
const fileSystem = new Trie<string>([
|
||||
'/home/user/documents/file1.txt',
|
||||
'/home/user/documents/file2.txt',
|
||||
'/home/user/pictures/photo.jpg',
|
||||
'/home/user/pictures/vacation/',
|
||||
'/home/user/downloads'
|
||||
]);
|
||||
|
||||
// Find common directory prefix
|
||||
expect(fileSystem.getLongestCommonPrefix()).toBe('/home/user/');
|
||||
|
||||
// List all files in a directory
|
||||
const documentsFiles = fileSystem.getWords('/home/user/documents/');
|
||||
expect(documentsFiles).toEqual(['/home/user/documents/file1.txt', '/home/user/documents/file2.txt']);
|
||||
});
|
||||
|
||||
test('@example Autocomplete: Basic word suggestions', () => {
|
||||
// Create a trie for autocomplete
|
||||
const autocomplete = new Trie<string>([
|
||||
'function',
|
||||
'functional',
|
||||
'functions',
|
||||
'class',
|
||||
'classes',
|
||||
'classical',
|
||||
'closure',
|
||||
'const',
|
||||
'constructor'
|
||||
]);
|
||||
|
||||
// Test autocomplete with different prefixes
|
||||
expect(autocomplete.getWords('fun')).toEqual(['functional', 'functions', 'function']);
|
||||
expect(autocomplete.getWords('cla')).toEqual(['classes', 'classical', 'class']);
|
||||
expect(autocomplete.getWords('con')).toEqual(['constructor', 'const']);
|
||||
|
||||
// Test with non-matching prefix
|
||||
expect(autocomplete.getWords('xyz')).toEqual([]);
|
||||
});
|
||||
|
||||
test('@example Dictionary: Case-insensitive word lookup', () => {
|
||||
// Create a case-insensitive dictionary
|
||||
const dictionary = new Trie<string>([], { caseSensitive: false });
|
||||
|
||||
// Add words with mixed casing
|
||||
dictionary.add('Hello');
|
||||
dictionary.add('WORLD');
|
||||
dictionary.add('JavaScript');
|
||||
|
||||
// Test lookups with different casings
|
||||
expect(dictionary.has('hello')).toBe(true);
|
||||
expect(dictionary.has('HELLO')).toBe(true);
|
||||
expect(dictionary.has('Hello')).toBe(true);
|
||||
expect(dictionary.has('javascript')).toBe(true);
|
||||
expect(dictionary.has('JAVASCRIPT')).toBe(true);
|
||||
});
|
||||
|
||||
test('@example IP Address Routing Table', () => {
|
||||
// Add IP address prefixes and their corresponding routes
|
||||
const routes = {
|
||||
'192.168.1': 'LAN_SUBNET_1',
|
||||
'192.168.2': 'LAN_SUBNET_2',
|
||||
'10.0.0': 'PRIVATE_NETWORK_1',
|
||||
'10.0.1': 'PRIVATE_NETWORK_2'
|
||||
};
|
||||
|
||||
const ipRoutingTable = new Trie<string>(Object.keys(routes));
|
||||
|
||||
// Check IP address prefix matching
|
||||
expect(ipRoutingTable.hasPrefix('192.168.1')).toBe(true);
|
||||
expect(ipRoutingTable.hasPrefix('192.168.2')).toBe(true);
|
||||
|
||||
// Validate IP address belongs to subnet
|
||||
const ip = '192.168.1.100';
|
||||
const subnet = ip.split('.').slice(0, 3).join('.');
|
||||
expect(ipRoutingTable.hasPrefix(subnet)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue