Compare commits

..

3 commits

14 changed files with 408 additions and 139 deletions

View file

@ -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.6](https://github.com/zrwusa/data-structure-typed/compare/v1.51.5...main) (upcoming)
## [v1.53.7](https://github.com/zrwusa/data-structure-typed/compare/v1.51.5...main) (upcoming)
### Changes

52
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "data-structure-typed",
"version": "1.53.6",
"version": "1.53.7",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "data-structure-typed",
"version": "1.53.6",
"version": "1.53.7",
"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.5",
"avl-tree-typed": "^1.53.6",
"benchmark": "^2.1.4",
"binary-tree-typed": "^1.53.5",
"bst-typed": "^1.53.5",
"data-structure-typed": "^1.53.5",
"binary-tree-typed": "^1.53.6",
"bst-typed": "^1.53.6",
"data-structure-typed": "^1.53.6",
"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.5",
"heap-typed": "^1.53.6",
"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.5",
"resolved": "https://registry.npmjs.org/avl-tree-typed/-/avl-tree-typed-1.53.5.tgz",
"integrity": "sha512-BkJgD5S4ALEl4ytKKVvqz3Qo/19U2bxnOUyZxy3dMgXNh20vaPZAJ82bFRX2lAoBFBDmIgtWp/klxC0CrDO6nQ==",
"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==",
"dev": true,
"license": "MIT",
"dependencies": {
"data-structure-typed": "^1.53.5"
"data-structure-typed": "^1.53.6"
}
},
"node_modules/babel-jest": {
@ -3602,13 +3602,13 @@
}
},
"node_modules/binary-tree-typed": {
"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==",
"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==",
"dev": true,
"license": "MIT",
"dependencies": {
"data-structure-typed": "^1.53.5"
"data-structure-typed": "^1.53.6"
}
},
"node_modules/brace-expansion": {
@ -3691,13 +3691,13 @@
}
},
"node_modules/bst-typed": {
"version": "1.53.5",
"resolved": "https://registry.npmjs.org/bst-typed/-/bst-typed-1.53.5.tgz",
"integrity": "sha512-poYNwY4rwHinpNCQDsTv631XGUNJh/wvklLzaok+Nt+XV1PsH2e8VATaTCUMdTwLFrDn7jGFoY8mJkJjmr0s2w==",
"version": "1.53.6",
"resolved": "https://registry.npmjs.org/bst-typed/-/bst-typed-1.53.6.tgz",
"integrity": "sha512-LKY9GMNdhYlb8Iprc8Z4143bQ6cWYhE8thSHb8jhvVzsEuqXqetM0tSG88a5QjURgxt+Mr9GGJruSeBYX7/vQw==",
"dev": true,
"license": "MIT",
"dependencies": {
"data-structure-typed": "^1.53.5"
"data-structure-typed": "^1.53.6"
}
},
"node_modules/buffer-from": {
@ -4069,9 +4069,9 @@
}
},
"node_modules/data-structure-typed": {
"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==",
"version": "1.53.6",
"resolved": "https://registry.npmjs.org/data-structure-typed/-/data-structure-typed-1.53.6.tgz",
"integrity": "sha512-o3j49Yb5nPWyMIYvm6KOqlaj6SbUP/nq26kl6KmT0UknbOPfrElGIAdYwV9J87dHkHxGPGJG7Ee754jv8XjIIQ==",
"dev": true,
"license": "MIT"
},
@ -5946,13 +5946,13 @@
}
},
"node_modules/heap-typed": {
"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==",
"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==",
"dev": true,
"license": "MIT",
"dependencies": {
"data-structure-typed": "^1.53.5"
"data-structure-typed": "^1.53.6"
}
},
"node_modules/html-escaper": {

View file

@ -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.5",
"avl-tree-typed": "^1.53.6",
"benchmark": "^2.1.4",
"binary-tree-typed": "^1.53.5",
"bst-typed": "^1.53.5",
"data-structure-typed": "^1.53.5",
"binary-tree-typed": "^1.53.6",
"bst-typed": "^1.53.6",
"data-structure-typed": "^1.53.6",
"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.5",
"heap-typed": "^1.53.6",
"istanbul-badges-readme": "^1.9.0",
"jest": "^29.7.0",
"js-sdsl": "^4.4.2",

View file

@ -254,7 +254,14 @@ function updateExamples(testDir: string, sourceBaseDir: string): void {
* Replace content between markers in README.md.
*/
function replaceExamplesInReadme(readmePath: string, newExamples: string[]): void {
const readmeContent = fs.readFileSync(readmePath, 'utf-8');
let readmeContent: string;
try {
readmeContent = fs.readFileSync(readmePath, 'utf-8');
} catch (error) {
console.warn(`Failed to read ${fileName} at ${readmePath}: ${error}`);
return;
}
const startIdx = readmeContent.indexOf(START_MARKER);
const endIdx = readmeContent.indexOf(END_MARKER);
@ -269,7 +276,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(`README.md updated with new examples.`);
console.log(`${fileName} updated with new examples.`);
}
// Run the script

View file

@ -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.
*/
override keyValueNodeEntryRawToNodeAndValue(
protected 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;

View file

@ -216,7 +216,7 @@ export class BinaryTree<
* input parameter (`keyNodeEntryOrRaw`) and processes it accordingly to return a node or null
* value.
*/
keyValueNodeEntryRawToNodeAndValue(
protected _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

View file

@ -112,15 +112,11 @@ 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]);
*
* // 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]
* 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]
* @example
* // Find lowest common ancestor
* const bst = new BST<number>([20, 10, 30, 5, 15, 25, 35, 3, 7, 12, 18]);
@ -237,11 +233,11 @@ export class BST<
* value associated with a key in a key-value pair.
* @returns either a NODE object or undefined.
*/
override keyValueNodeEntryRawToNodeAndValue(
protected 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];
}
@ -303,7 +299,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) {

View file

@ -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);

View file

@ -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.
*/
override keyValueNodeEntryRawToNodeAndValue(
protected 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);

View file

@ -514,6 +514,16 @@ 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;

View file

@ -92,13 +92,100 @@ 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 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
* 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 `
*/
constructor(words: Iterable<string> | Iterable<R> = [], options?: TrieOptions<R>) {
super(options);
@ -107,13 +194,7 @@ export class Trie<R = any> extends IterableElementBase<string, R, Trie<R>> {
if (caseSensitive !== undefined) this._caseSensitive = caseSensitive;
}
if (words) {
for (const word of words) {
if (this.toElementFn) {
this.add(this.toElementFn(word as R));
} else {
this.add(word as string);
}
}
this.addMany(words);
}
}
@ -175,6 +256,30 @@ 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.

View file

@ -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 });

View file

@ -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', () => {

View file

@ -932,3 +932,154 @@ 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);
});
});