From 1a1ea21444c8d26ffacfa253849945b649e70343 Mon Sep 17 00:00:00 2001 From: Revone Date: Mon, 25 Nov 2024 01:10:33 +1300 Subject: [PATCH] fix: Adjust type safety for the map method of all binary trees, ensuring it no longer returns the TREE type to its parent class and limiting Node type nesting to a maximum of 10 levels. Fix the issue with RedBlackTree objects being incorrectly compared when used as keys. Rename extractComparable to specificComparable. Correct the order of key and value in callback functions for the map and filter methods. --- CHANGELOG.md | 2 +- CONTRIBUTING.md | 2 +- package-lock.json | 52 ++-- package.json | 12 +- .../binary-tree/avl-tree-multi-map.ts | 135 +++++---- src/data-structures/binary-tree/avl-tree.ts | 47 +++- .../binary-tree/binary-tree.ts | 171 ++++++------ src/data-structures/binary-tree/bst.ts | 161 ++++++----- .../binary-tree/red-black-tree.ts | 130 +++++---- .../binary-tree/tree-multi-map.ts | 128 +++++---- src/data-structures/graph/abstract-graph.ts | 4 +- .../linked-list/doubly-linked-list.ts | 26 +- .../linked-list/singly-linked-list.ts | 34 +-- src/interfaces/binary-tree.ts | 16 +- src/types/data-structures/base/base.ts | 2 +- .../binary-tree/avl-tree-multi-map.ts | 6 +- .../data-structures/binary-tree/avl-tree.ts | 6 +- .../binary-tree/binary-tree.ts | 6 +- src/types/data-structures/binary-tree/bst.ts | 8 +- .../data-structures/binary-tree/rb-tree.ts | 10 +- .../binary-tree/tree-multi-map.ts | 10 +- test/integration/index.html | 6 +- .../binary-tree/binary-tree-overall.test.ts | 6 +- .../binary-tree/avl-tree-multi-map.test.ts | 14 +- .../binary-tree/avl-tree.test.ts | 10 +- .../binary-tree/binary-tree.test.ts | 10 +- .../data-structures/binary-tree/bst.test.ts | 12 +- .../data/cost-of-living-by-country.ts | 259 ++++++++++++++++++ .../binary-tree/overall.test.ts | 2 + .../binary-tree/red-black-tree.test.ts | 30 +- .../binary-tree/tree-multi-map.test.ts | 10 +- .../graph/directed-graph.test.ts | 6 +- 32 files changed, 842 insertions(+), 491 deletions(-) create mode 100644 test/unit/data-structures/binary-tree/data/cost-of-living-by-country.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ab45579..71064cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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.8](https://github.com/zrwusa/data-structure-typed/compare/v1.51.5...main) (upcoming) ### Changes diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 59f216c..89c86a8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -86,7 +86,7 @@ - Make your pull requests to be **specific** and **focused**. Instead of contributing "several data structures" all at once contribute them all - one by one separately (i.e. one pull request for "RBTree", another one + one by one separately (i.e. one pull request for "RedBlackTree", another one for "AATree" and so on). - Modify **README.md** for each of the data structure **with explanations** of the algorithm and **with links** to further readings. diff --git a/package-lock.json b/package-lock.json index 2d3376f..549eb25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "data-structure-typed", - "version": "1.53.7", + "version": "1.53.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "data-structure-typed", - "version": "1.53.7", + "version": "1.53.8", "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.7", "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.7", + "bst-typed": "^1.53.7", + "data-structure-typed": "^1.53.7", "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.7", "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.7", + "resolved": "https://registry.npmjs.org/avl-tree-typed/-/avl-tree-typed-1.53.7.tgz", + "integrity": "sha512-+HyMQY+1FoOQm6ESwSbtJL6LnR9XNYxqJc6IdDEAhBZovRx2m3bwgse5q+3SLyEqQAUwSY9LsGD3A80UQq4fjQ==", "dev": true, "license": "MIT", "dependencies": { - "data-structure-typed": "^1.53.6" + "data-structure-typed": "^1.53.7" } }, "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.7", + "resolved": "https://registry.npmjs.org/binary-tree-typed/-/binary-tree-typed-1.53.7.tgz", + "integrity": "sha512-Pd/N1QOFm7qgBhdp4T43lBpUhPUex5hwL/9JuZ7qgfN++qxCQLAwU72tgPAvSjwb4z6njqG8/4hfLdnLUbDWUg==", "dev": true, "license": "MIT", "dependencies": { - "data-structure-typed": "^1.53.6" + "data-structure-typed": "^1.53.7" } }, "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.7", + "resolved": "https://registry.npmjs.org/bst-typed/-/bst-typed-1.53.7.tgz", + "integrity": "sha512-Sj2nJb/2SUAqcSg4oXNWMt9OxlF4NcxPQ6o2wMKbqiKp4JCblmLpVEjbaZBVrpRMTKHEZctW/is21QPcxQ9Hnw==", "dev": true, "license": "MIT", "dependencies": { - "data-structure-typed": "^1.53.6" + "data-structure-typed": "^1.53.7" } }, "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.7", + "resolved": "https://registry.npmjs.org/data-structure-typed/-/data-structure-typed-1.53.7.tgz", + "integrity": "sha512-8higtCbppFIeHaz7JMJL2WdZ/vc4wtO8+Yh6qZtONDAy+shqafASqpTsYnKJaE8gNiPzOkP2YOMiEbVdpKLhhQ==", "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.7", + "resolved": "https://registry.npmjs.org/heap-typed/-/heap-typed-1.53.7.tgz", + "integrity": "sha512-iXPgPtyCxDY/+e8eCvWUVSsQ2h+7L6upoP1QGJrIm47+tdTovV/EZ0PhCIKWERRzHI4Us/PM9hZbX0Jt8KBeRg==", "dev": true, "license": "MIT", "dependencies": { - "data-structure-typed": "^1.53.6" + "data-structure-typed": "^1.53.7" } }, "node_modules/html-escaper": { diff --git a/package.json b/package.json index 360b14d..385974a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "data-structure-typed", - "version": "1.53.8", + "version": "1.53.9", "description": "Javascript Data Structure. Heap, Binary Tree, Red Black Tree, Linked List, Deque, Trie, HashMap, Directed Graph, Undirected Graph, Binary Search Tree(BST), AVL Tree, Priority Queue, Graph, Queue, Tree Multiset, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue, Stack. Benchmark compared with C++ STL. API aligned with ES6 and Java.util. Usability is comparable to Python", "main": "dist/cjs/index.js", "module": "dist/mjs/index.js", @@ -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.7", "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.7", + "bst-typed": "^1.53.7", + "data-structure-typed": "^1.53.7", "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.7", "istanbul-badges-readme": "^1.9.0", "jest": "^29.7.0", "js-sdsl": "^4.4.2", diff --git a/src/data-structures/binary-tree/avl-tree-multi-map.ts b/src/data-structures/binary-tree/avl-tree-multi-map.ts index cc40171..00d0465 100644 --- a/src/data-structures/binary-tree/avl-tree-multi-map.ts +++ b/src/data-structures/binary-tree/avl-tree-multi-map.ts @@ -6,12 +6,12 @@ * @license MIT License */ import type { - AVLTreeMultiMapNested, AVLTreeMultiMapNodeNested, AVLTreeMultiMapOptions, BinaryTreeDeleteResult, BSTNOptKeyOrNode, BTNRep, + EntryCallback, IterationType } from '../../types'; import { IBinaryTree } from '../../interfaces'; @@ -64,17 +64,10 @@ export class AVLTreeMultiMap< K = any, V = any, R = object, - NODE extends AVLTreeMultiMapNode = AVLTreeMultiMapNode>, - TREE extends AVLTreeMultiMap = AVLTreeMultiMap< - K, - V, - R, - NODE, - AVLTreeMultiMapNested - > + NODE extends AVLTreeMultiMapNode = AVLTreeMultiMapNode> > - extends AVLTree - implements IBinaryTree + extends AVLTree + implements IBinaryTree { /** * The constructor initializes a new AVLTreeMultiMap object with optional initial elements. @@ -139,15 +132,16 @@ export class AVLTreeMultiMap< * @returns a new instance of the AVLTreeMultiMap class, with the specified options, as a TREE * object. */ - override createTree(options?: AVLTreeMultiMapOptions): TREE { - return new AVLTreeMultiMap([], { + // @ts-ignore + override createTree(options?: AVLTreeMultiMapOptions) { + return new AVLTreeMultiMap([], { iterationType: this.iterationType, isMapMode: this._isMapMode, - extractComparable: this._extractComparable, + specifyComparable: this._specifyComparable, toEntryFn: this._toEntryFn, isReverse: this._isReverse, ...options - }) as TREE; + }); } /** @@ -161,44 +155,6 @@ export class AVLTreeMultiMap< return keyNodeEntryOrRaw instanceof AVLTreeMultiMapNode; } - /** - * The function `keyValueNodeEntryRawToNodeAndValue` converts a key, value, entry, or raw element into - * a node object. - * @param {BTNRep | R} keyNodeEntryOrRaw - The - * `keyNodeEntryOrRaw` parameter can be of type `R` or `BTNRep`. - * @param {V} [value] - The `value` parameter is an optional value that can be passed to the - * `override` function. It represents the value associated with the key in the data structure. If no - * value is provided, it will default to `undefined`. - * @param [count=1] - The `count` parameter is an optional parameter that specifies the number of - * 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( - keyNodeEntryOrRaw: BTNRep | R, - value?: V, - count = 1 - ): [NODE | undefined, V | undefined] { - if (keyNodeEntryOrRaw === undefined || keyNodeEntryOrRaw === null) return [undefined, undefined]; - if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; - - if (this.isEntry(keyNodeEntryOrRaw)) { - const [key, entryValue] = keyNodeEntryOrRaw; - if (key === undefined || key === null) return [undefined, undefined]; - const finalValue = value ?? entryValue; - return [this.createNode(key, finalValue, count), finalValue]; - } - - if (this.isRaw(keyNodeEntryOrRaw)) { - const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw); - const finalValue = value ?? entryValue; - if (this.isKey(key)) return [this.createNode(key, finalValue, count), finalValue]; - } - - if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value, count), value]; - - return [undefined, undefined]; - } - /** * Time Complexity: O(log n) * Space Complexity: O(1) @@ -374,7 +330,8 @@ export class AVLTreeMultiMap< * The function overrides the clone method to create a deep copy of a tree object. * @returns The `clone()` method is returning a cloned instance of the `TREE` object. */ - override clone(): TREE { + // @ts-ignore + override clone() { const cloned = this.createTree(); if (this._isMapMode) this.bfs(node => cloned.add(node.key, undefined, node.count)); else this.bfs(node => cloned.add(node.key, node.value, node.count)); @@ -382,6 +339,76 @@ export class AVLTreeMultiMap< return cloned; } + /** + * The `map` function in TypeScript overrides the default behavior to create a new AVLTreeMultiMap + * with modified entries based on a provided callback. + * @param callback - The `callback` parameter is a function that will be called for each entry in the + * AVLTreeMultiMap. It takes four arguments: + * @param [options] - The `options` parameter in the `override map` function is of type + * `AVLTreeMultiMapOptions`. This parameter allows you to provide additional + * configuration options when creating a new `AVLTreeMultiMap` instance within the `map` function. + * These options + * @param {any} [thisArg] - The `thisArg` parameter in the `override map` function is used to specify + * the value of `this` when executing the `callback` function. It allows you to set the context + * (value of `this`) for the callback function. This can be useful when you want to access properties + * or + * @returns The `map` method is returning a new `AVLTreeMultiMap` instance with the entries + * transformed by the provided `callback` function. Each entry in the original tree is passed to the + * `callback` function along with the index and the original tree itself. The transformed entries are + * then added to the new `AVLTreeMultiMap` instance, which is returned at the end. + */ + // @ts-ignore + override map( + callback: EntryCallback, + options?: AVLTreeMultiMapOptions, + thisArg?: any + ) { + const newTree = new AVLTreeMultiMap([], options); + let index = 0; + for (const [key, value] of this) { + newTree.add(callback.call(thisArg, key, value, index++, this)); + } + return newTree; + } + + /** + * The function `keyValueNodeEntryRawToNodeAndValue` converts a key, value, entry, or raw element into + * a node object. + * @param {BTNRep | R} keyNodeEntryOrRaw - The + * `keyNodeEntryOrRaw` parameter can be of type `R` or `BTNRep`. + * @param {V} [value] - The `value` parameter is an optional value that can be passed to the + * `override` function. It represents the value associated with the key in the data structure. If no + * value is provided, it will default to `undefined`. + * @param [count=1] - The `count` parameter is an optional parameter that specifies the number of + * 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( + keyNodeEntryOrRaw: BTNRep | R, + value?: V, + count = 1 + ): [NODE | undefined, V | undefined] { + if (keyNodeEntryOrRaw === undefined || keyNodeEntryOrRaw === null) return [undefined, undefined]; + if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; + + if (this.isEntry(keyNodeEntryOrRaw)) { + const [key, entryValue] = keyNodeEntryOrRaw; + if (key === undefined || key === null) return [undefined, undefined]; + const finalValue = value ?? entryValue; + return [this.createNode(key, finalValue, count), finalValue]; + } + + if (this.isRaw(keyNodeEntryOrRaw)) { + const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw); + const finalValue = value ?? entryValue; + if (this.isKey(key)) return [this.createNode(key, finalValue, count), finalValue]; + } + + if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value, count), value]; + + return [undefined, undefined]; + } + /** * Time Complexity: O(1) * Space Complexity: O(1) diff --git a/src/data-structures/binary-tree/avl-tree.ts b/src/data-structures/binary-tree/avl-tree.ts index 718402a..ae79479 100644 --- a/src/data-structures/binary-tree/avl-tree.ts +++ b/src/data-structures/binary-tree/avl-tree.ts @@ -7,12 +7,12 @@ */ import { BST, BSTNode } from './bst'; import type { - AVLTreeNested, AVLTreeNodeNested, AVLTreeOptions, BinaryTreeDeleteResult, BSTNOptKeyOrNode, - BTNRep + BTNRep, + EntryCallback } from '../../types'; import { IBinaryTree } from '../../interfaces'; @@ -67,11 +67,10 @@ export class AVLTree< K = any, V = any, R = object, - NODE extends AVLTreeNode = AVLTreeNode>, - TREE extends AVLTree = AVLTree> + NODE extends AVLTreeNode = AVLTreeNode> > - extends BST - implements IBinaryTree + extends BST + implements IBinaryTree { /** * This is a constructor function for an AVLTree class that initializes the tree with keys, nodes, @@ -103,21 +102,25 @@ export class AVLTree< } /** - * The function creates a new AVL tree with the specified options and returns it. - * @param {AVLTreeOptions} [options] - The `options` parameter is an optional object that can be - * passed to the `createTree` function. It is used to customize the behavior of the AVL tree that is - * being created. - * @returns a new AVLTree object. + * The function `createTree` in TypeScript overrides the default AVLTree creation with the provided + * options. + * @param [options] - The `options` parameter in the `createTree` function is an object that contains + * configuration options for creating an AVL tree. These options can include properties such as + * `iterationType`, `isMapMode`, `specifyComparable`, `toEntryFn`, and `isReverse`. The function + * creates a + * @returns An AVLTree object is being returned with the specified options and properties inherited + * from the current object. */ - override createTree(options?: AVLTreeOptions): TREE { - return new AVLTree([], { + // @ts-ignore + override createTree(options?: AVLTreeOptions) { + return new AVLTree([], { iterationType: this.iterationType, isMapMode: this._isMapMode, - extractComparable: this._extractComparable, + specifyComparable: this._specifyComparable, toEntryFn: this._toEntryFn, isReverse: this._isReverse, ...options - }) as TREE; + }); } /** @@ -174,6 +177,20 @@ export class AVLTree< return deletedResults; } + // @ts-ignore + override map( + callback: EntryCallback, + options?: AVLTreeOptions, + thisArg?: any + ) { + const newTree = new AVLTree([], options); + let index = 0; + for (const [key, value] of this) { + newTree.add(callback.call(thisArg, key, value, index++, this)); + } + return newTree; + } + /** * Time Complexity: O(1) * Space Complexity: O(1) diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index 4a2566d..a9595ea 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -8,7 +8,6 @@ import { BinaryTreeDeleteResult, - BinaryTreeNested, BinaryTreeNodeNested, BinaryTreeOptions, BinaryTreePrintOptions, @@ -105,11 +104,10 @@ export class BinaryTree< K = any, V = any, R = object, - NODE extends BinaryTreeNode = BinaryTreeNode>, - TREE extends BinaryTree = BinaryTree> + NODE extends BinaryTreeNode = BinaryTreeNode> > extends IterableEntryBase - implements IBinaryTree + implements IBinaryTree { iterationType: IterationType = 'ITERATIVE'; @@ -172,6 +170,9 @@ export class BinaryTree< } /** + * Time Complexity: O(1) + * Space Complexity: O(1) + * * The function creates a new binary tree node with a specified key and optional value. * @param {K} key - The `key` parameter is the key of the node being created in the binary tree. * @param {V} [value] - The `value` parameter in the `createNode` function is optional, meaning it is @@ -185,63 +186,26 @@ export class BinaryTree< } /** - * The function creates a binary tree with the specified options. - * @param [options] - The `options` parameter in the `createTree` function is an optional parameter - * that allows you to provide partial configuration options for creating a binary tree. It is of type - * `Partial>`, which means you can pass in an object containing a subset - * of properties - * @returns A new instance of a binary tree with the specified options is being returned. + * Time Complexity: O(1) + * Space Complexity: O(1) + * + * The `createTree` function creates a new binary tree based on the provided options. + * @param [options] - The `options` parameter in the `createTree` method is of type + * `BinaryTreeOptions`. This type likely contains configuration options for creating a + * binary tree, such as the iteration type, whether the tree is in map mode, and functions for + * converting entries. + * @returns The `createTree` method is returning an instance of the `BinaryTree` class with the + * provided options. The method is creating a new `BinaryTree` object with an empty array as the + * initial data, and then setting various options such as `iterationType`, `isMapMode`, and + * `toEntryFn` based on the current object's properties and the provided `options`. Finally, it */ - createTree(options?: BinaryTreeOptions): TREE { - return new BinaryTree([], { + createTree(options?: BinaryTreeOptions): typeof this { + return new BinaryTree([], { iterationType: this.iterationType, isMapMode: this._isMapMode, toEntryFn: this._toEntryFn, ...options - }) as TREE; - } - - /** - * The function `keyValueNodeEntryRawToNodeAndValue` converts various input types into a node object - * or returns null. - * @param {BTNRep | R} keyNodeEntryOrRaw - The - * `keyValueNodeEntryRawToNodeAndValue` function takes in a parameter `keyNodeEntryOrRaw`, which - * can be of type `BTNRep` or `R`. This parameter represents either a key, a - * node, an entry - * @param {V} [value] - The `value` parameter in the `keyValueNodeEntryRawToNodeAndValue` function is - * an optional parameter of type `V`. It represents the value associated with the key in the node - * being created. If a `value` is provided, it will be used when creating the node. If - * @returns The `keyValueNodeEntryRawToNodeAndValue` function returns an optional node - * (`OptNodeOrNull`) based on the input parameters provided. The function checks the type of the - * input parameter (`keyNodeEntryOrRaw`) and processes it accordingly to return a node or null - * value. - */ - protected _keyValueNodeEntryRawToNodeAndValue( - keyNodeEntryOrRaw: BTNRep | R, - value?: V - ): [OptNodeOrNull, V | undefined] { - if (keyNodeEntryOrRaw === undefined) return [undefined, undefined]; - if (keyNodeEntryOrRaw === null) return [null, undefined]; - - if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; - - if (this.isEntry(keyNodeEntryOrRaw)) { - const [key, entryValue] = keyNodeEntryOrRaw; - if (key === undefined) return [undefined, undefined]; - else if (key === null) return [null, undefined]; - const finalValue = value ?? entryValue; - return [this.createNode(key, finalValue), finalValue]; - } - - if (this.isRaw(keyNodeEntryOrRaw)) { - const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw); - const finalValue = value ?? entryValue; - if (this.isKey(key)) return [this.createNode(key, finalValue), finalValue]; - } - - if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value), value]; - - return [undefined, undefined]; + }) as unknown as typeof this; } /** @@ -526,7 +490,7 @@ export class BinaryTree< * elements from the other tree. * @param anotherTree - `BinaryTree` */ - merge(anotherTree: BinaryTree) { + merge(anotherTree: this) { this.addMany(anotherTree, []); } @@ -1635,7 +1599,7 @@ export class BinaryTree< * original tree using breadth-first search (bfs), and adds the nodes to the new tree. If a node in * the original tree is null, a null node is added to the cloned tree. If a node */ - clone(): TREE { + clone() { const cloned = this.createTree(); this.bfs( node => { @@ -1673,7 +1637,7 @@ export class BinaryTree< const newTree = this.createTree(); let index = 0; for (const [key, value] of this) { - if (predicate.call(thisArg, value, key, index++, this)) { + if (predicate.call(thisArg, key, value, index++, this)) { newTree.add([key, value]); } } @@ -1684,36 +1648,34 @@ export class BinaryTree< * Time Complexity: O(n) * Space Complexity: O(n) * - * The `map` function iterates over key-value pairs in a tree data structure, applies a callback - * function to each value, and returns a new tree with the updated values. - * @param callback - The `callback` parameter in the `map` method is a function that will be called - * on each entry in the tree. It takes four arguments: - * @param {any} [thisArg] - The `thisArg` parameter in the `map` function is an optional parameter - * that specifies the value to be passed as `this` when executing the callback function. If provided, - * the `thisArg` value will be used as the `this` value within the callback function. If `thisArg - * @returns The `map` method is returning a new tree with the entries modified by the provided - * callback function. Each entry in the original tree is passed to the callback function, and the - * result of the callback function is added to the new tree. + * The `map` function in TypeScript creates a new BinaryTree by applying a callback function to each + * entry in the original BinaryTree. + * @param callback - A function that will be called for each entry in the current binary tree. It + * takes the key, value (which can be undefined), and an array containing the mapped key and value as + * arguments. + * @param [options] - The `options` parameter in the `map` method is of type `BinaryTreeOptions`. It is an optional parameter that allows you to specify additional options for the binary + * tree being created during the mapping process. These options could include things like custom + * comparators, initial + * @param {any} [thisArg] - The `thisArg` parameter in the `map` method is used to specify the value + * of `this` when executing the `callback` function. It allows you to set the context (value of + * `this`) within the callback function. If `thisArg` is provided, it will be passed + * @returns The `map` function is returning a new `BinaryTree` instance filled with entries that are + * the result of applying the provided `callback` function to each entry in the original tree. */ - map(callback: EntryCallback, thisArg?: any) { - const newTree = this.createTree(); + map( + callback: EntryCallback, + options?: BinaryTreeOptions, + thisArg?: any + ) { + const newTree = new BinaryTree([], options); let index = 0; for (const [key, value] of this) { - newTree.add([key, callback.call(thisArg, value, key, index++, this)]); + newTree.add(callback.call(thisArg, key, value, index++, this)); } return newTree; } - // // TODO Type error, need to return a TREE that is a value type only for callback function. - // // map(callback: (entry: [K, V | undefined], tree: this) => NV) { - // // const newTree = this.createTree(); - // // for (const [key, value] of this) { - // // newTree.add(key, callback([key, value], this)); - // // } - // // return newTree; - // // } - // - /** * Time Complexity: O(n) * Space Complexity: O(n) @@ -1743,7 +1705,7 @@ export class BinaryTree< if (opts.isShowRedBlackNIL) output += `S for Sentinel Node(NIL)\n`; const display = (root: OptNodeOrNull): void => { - const [lines, , ,] = this._displayAux(root, opts); + const [lines, ,] = this._displayAux(root, opts); let paragraph = ''; for (const line of lines) { paragraph += line + '\n'; @@ -1774,6 +1736,49 @@ export class BinaryTree< console.log(this.toVisual(startNode, options)); } + /** + * The function `keyValueNodeEntryRawToNodeAndValue` converts various input types into a node object + * or returns null. + * @param {BTNRep | R} keyNodeEntryOrRaw - The + * `keyValueNodeEntryRawToNodeAndValue` function takes in a parameter `keyNodeEntryOrRaw`, which + * can be of type `BTNRep` or `R`. This parameter represents either a key, a + * node, an entry + * @param {V} [value] - The `value` parameter in the `keyValueNodeEntryRawToNodeAndValue` function is + * an optional parameter of type `V`. It represents the value associated with the key in the node + * being created. If a `value` is provided, it will be used when creating the node. If + * @returns The `keyValueNodeEntryRawToNodeAndValue` function returns an optional node + * (`OptNodeOrNull`) based on the input parameters provided. The function checks the type of the + * input parameter (`keyNodeEntryOrRaw`) and processes it accordingly to return a node or null + * value. + */ + protected _keyValueNodeEntryRawToNodeAndValue( + keyNodeEntryOrRaw: BTNRep | R, + value?: V + ): [OptNodeOrNull, V | undefined] { + if (keyNodeEntryOrRaw === undefined) return [undefined, undefined]; + if (keyNodeEntryOrRaw === null) return [null, undefined]; + + if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; + + if (this.isEntry(keyNodeEntryOrRaw)) { + const [key, entryValue] = keyNodeEntryOrRaw; + if (key === undefined) return [undefined, undefined]; + else if (key === null) return [null, undefined]; + const finalValue = value ?? entryValue; + return [this.createNode(key, finalValue), finalValue]; + } + + if (this.isRaw(keyNodeEntryOrRaw)) { + const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw); + const finalValue = value ?? entryValue; + if (this.isKey(key)) return [this.createNode(key, finalValue), finalValue]; + } + + if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value), value]; + + return [undefined, undefined]; + } + /** * Time complexity: O(n) * Space complexity: O(n) diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index a7288f3..cfbf55c 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -5,8 +5,7 @@ * @copyright Copyright (c) 2022 Pablo Zeng * @license MIT License */ -import type { - BSTNested, +import { BSTNodeNested, BSTNOptKeyOrNode, BSTOptions, @@ -15,6 +14,7 @@ import type { Comparator, CP, DFSOrderPattern, + EntryCallback, IterationType, NodeCallback, NodePredicate, @@ -151,15 +151,9 @@ export class BSTNode = BSTNod * console.log(findLCA(5, 35)); // 15 * console.log(findLCA(20, 30)); // 25 */ -export class BST< - K = any, - V = any, - R = object, - NODE extends BSTNode = BSTNode>, - TREE extends BST = BST> - > - extends BinaryTree - implements IBinaryTree +export class BST = BSTNode>> + extends BinaryTree + implements IBinaryTree { /** * This is the constructor function for a Binary Search Tree class in TypeScript. @@ -173,8 +167,8 @@ export class BST< super([], options); if (options) { - const { extractComparable, isReverse } = options; - if (typeof extractComparable === 'function') this._extractComparable = extractComparable; + const { specifyComparable, isReverse } = options; + if (typeof specifyComparable === 'function') this._specifyComparable = specifyComparable; if (isReverse !== undefined) this._isReverse = isReverse; } @@ -202,6 +196,45 @@ export class BST< return this._isReverse; } + protected _comparator: Comparator = (a: K, b: K): number => { + if (isComparable(a) && isComparable(b)) { + if (a > b) return 1; + if (a < b) return -1; + return 0; + } + if (this._specifyComparable) { + if (this._specifyComparable(a) > this._specifyComparable(b)) return 1; + if (this._specifyComparable(a) < this._specifyComparable(b)) return -1; + return 0; + } + if (typeof a === 'object' || typeof b === 'object') { + throw TypeError( + `When comparing object types, a custom specifyComparable must be defined in the constructor's options parameter.` + ); + } + + return 0; + }; + + /** + * The function returns the value of the _comparator property. + * @returns The `_comparator` property is being returned. + */ + get comparator() { + return this._comparator; + } + + protected _specifyComparable?: (key: K) => Comparable; + + /** + * This function returns the value of the `_specifyComparable` property. + * @returns The method `specifyComparable()` is being returned, which is a getter method for the + * `_specifyComparable` property. + */ + get specifyComparable() { + return this._specifyComparable; + } + /** * The function creates a new BSTNode with the given key and value and returns it. * @param {K} key - The key parameter is of type K, which represents the type of the key for the node @@ -215,39 +248,26 @@ export class BST< } /** - * The function creates a new binary search tree with the specified options. - * @param [options] - The `options` parameter is an optional object that allows you to customize the - * behavior of the `createTree` method. It accepts a partial `BSTOptions` object, which has the - * following properties: - * @returns a new instance of the BST class with the provided options. + * Time Complexity: O(1) + * Space Complexity: O(1) + * + * The `createTree` function in TypeScript overrides the default options with the provided options to + * create a new Binary Search Tree. + * @param [options] - The `options` parameter in the `createTree` method is an optional object that + * can contain the following properties: + * @returns A new instance of a Binary Search Tree (BST) is being returned with the specified options + * and properties inherited from the current instance. */ - override createTree(options?: BSTOptions): TREE { - return new BST([], { + // @ts-ignore + override createTree(options?: BSTOptions) { + return new BST([], { iterationType: this.iterationType, isMapMode: this._isMapMode, - extractComparable: this._extractComparable, + specifyComparable: this._specifyComparable, toEntryFn: this._toEntryFn, isReverse: this._isReverse, ...options - }) as TREE; - } - - /** - * The function overrides a method and converts a key, value pair or entry or raw element to a node. - * @param {BTNRep | R} keyNodeEntryOrRaw - A variable that can be of - * type R or BTNRep. It represents either a key, a node, an entry, or a raw - * element. - * @param {V} [value] - The `value` parameter is an optional value of type `V`. It represents the - * value associated with a key in a key-value pair. - * @returns either a NODE object or undefined. - */ - protected override _keyValueNodeEntryRawToNodeAndValue( - keyNodeEntryOrRaw: BTNRep | R, - value?: V - ): [OptNode, V | undefined] { - const [node, entryValue] = super._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value); - if (node === null) return [undefined, undefined]; - return [node, value ?? entryValue]; + }); } /** @@ -292,7 +312,7 @@ export class BST< * this._DEFAULT_COMPARATOR`. */ override isKey(key: any): key is K { - return isComparable(key, this._extractComparable !== undefined); + return isComparable(key, this._specifyComparable !== undefined); } /** @@ -472,7 +492,7 @@ export class BST< * @param anotherTree - `anotherTree` is an instance of a Binary Search Tree (BST) with key type `K`, * value type `V`, return type `R`, node type `NODE`, and tree type `TREE`. */ - override merge(anotherTree: BST) { + override merge(anotherTree: this) { this.addMany(anotherTree, [], false); } @@ -924,43 +944,36 @@ export class BST< return balanced; } - protected _comparator: Comparator = (a: K, b: K): number => { - if (isComparable(a) && isComparable(b)) { - if (a > b) return 1; - if (a < b) return -1; - return 0; + // @ts-ignore + override map( + callback: EntryCallback, + options?: BSTOptions, + thisArg?: any + ) { + const newTree = new BST([], options); + let index = 0; + for (const [key, value] of this) { + newTree.add(callback.call(thisArg, key, value, index++, this)); } - if (this._extractComparable) { - if (this._extractComparable(a) > this._extractComparable(b)) return 1; - if (this._extractComparable(a) < this._extractComparable(b)) return -1; - return 0; - } - if (typeof a === 'object' || typeof b === 'object') { - throw TypeError( - `When comparing object types, a custom extractComparable must be defined in the constructor's options parameter.` - ); - } - - return 0; - }; - - /** - * The function returns the value of the _comparator property. - * @returns The `_comparator` property is being returned. - */ - get comparator() { - return this._comparator; + return newTree; } - protected _extractComparable?: (key: K) => Comparable; - /** - * This function returns the value of the `_extractComparable` property. - * @returns The method `extractComparable()` is being returned, which is a getter method for the - * `_extractComparable` property. + * The function overrides a method and converts a key, value pair or entry or raw element to a node. + * @param {BTNRep | R} keyNodeEntryOrRaw - A variable that can be of + * type R or BTNRep. It represents either a key, a node, an entry, or a raw + * element. + * @param {V} [value] - The `value` parameter is an optional value of type `V`. It represents the + * value associated with a key in a key-value pair. + * @returns either a NODE object or undefined. */ - get extractComparable() { - return this._extractComparable; + protected override _keyValueNodeEntryRawToNodeAndValue( + keyNodeEntryOrRaw: BTNRep | R, + value?: V + ): [OptNode, V | undefined] { + const [node, entryValue] = super._keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw, value); + if (node === null) return [undefined, undefined]; + return [node, value ?? entryValue]; } /** diff --git a/src/data-structures/binary-tree/red-black-tree.ts b/src/data-structures/binary-tree/red-black-tree.ts index 7b96e88..ef3ea87 100644 --- a/src/data-structures/binary-tree/red-black-tree.ts +++ b/src/data-structures/binary-tree/red-black-tree.ts @@ -2,10 +2,10 @@ import type { BinaryTreeDeleteResult, BTNRep, CRUD, + EntryCallback, OptNode, RBTNColor, - RBTreeOptions, - RedBlackTreeNested, + RedBlackTreeOptions, RedBlackTreeNodeNested } from '../../types'; import { BST, BSTNode } from './bst'; @@ -83,18 +83,15 @@ export class RedBlackTreeNode< * * // Create a Red-Black Tree to index stock records by price * // Simulates a database index with stock price as the key for quick lookups - * const priceIndex = new RedBlackTree( - * marketStockData, - * { - * toEntryFn: stockRecord => [ - * stockRecord.price, // Use stock price as the key - * { - * ...stockRecord, - * lastUpdated: new Date() // Add a timestamp for when the record was indexed - * } - * ] - * } - * ); + * const priceIndex = new RedBlackTree(marketStockData, { + * toEntryFn: stockRecord => [ + * stockRecord.price, // Use stock price as the key + * { + * ...stockRecord, + * lastUpdated: new Date() // Add a timestamp for when the record was indexed + * } + * ] + * }); * * // Query the stock with the highest price * const highestPricedStock = priceIndex.getRightMost(); @@ -111,23 +108,22 @@ export class RedBlackTree< K = any, V = any, R = object, - NODE extends RedBlackTreeNode = RedBlackTreeNode>, - TREE extends RedBlackTree = RedBlackTree> + NODE extends RedBlackTreeNode = RedBlackTreeNode> > - extends BST - implements IBinaryTree + extends BST + implements IBinaryTree { /** * This is the constructor function for a Red-Black Tree data structure in TypeScript. * @param keysNodesEntriesOrRaws - The `keysNodesEntriesOrRaws` parameter is an * iterable object that can contain either keys, nodes, entries, or raw elements. It is used to - * initialize the RBTree with the provided elements. + * initialize the RedBlackTree with the provided elements. * @param [options] - The `options` parameter is an optional object that can be passed to the - * constructor. It is of type `RBTreeOptions`. This object can contain various options for + * constructor. It is of type `RedBlackTreeOptions`. This object can contain various options for * configuring the behavior of the Red-Black Tree. The specific properties and their meanings would * depend on the implementation */ - constructor(keysNodesEntriesOrRaws: Iterable> = [], options?: RBTreeOptions) { + constructor(keysNodesEntriesOrRaws: Iterable> = [], options?: RedBlackTreeOptions) { super([], options); this._root = this.NIL; @@ -166,19 +162,23 @@ export class RedBlackTree< } /** - * The function creates a new Red-Black Tree with the specified options. - * @param [options] - The `options` parameter is an optional object that contains additional - * configuration options for creating the Red-Black Tree. It has the following properties: - * @returns a new instance of a RedBlackTree object. + * The function `createTree` overrides the default implementation to create a Red-Black Tree with + * specified options in TypeScript. + * @param [options] - The `options` parameter in the `createTree` method is of type `RedBlackTreeOptions`, which is a generic type with three type parameters `K`, `V`, and `R`. This parameter + * allows you to pass additional configuration options when creating a new Red- + * @returns A new instance of a RedBlackTree with the specified options and properties from the + * current object is being returned. */ - override createTree(options?: RBTreeOptions): TREE { - return new RedBlackTree([], { + // @ts-ignore + override createTree(options?: RedBlackTreeOptions) { + return new RedBlackTree([], { iterationType: this.iterationType, isMapMode: this._isMapMode, - extractComparable: this._extractComparable, + specifyComparable: this._specifyComparable, toEntryFn: this._toEntryFn, ...options - }) as TREE; + }); } /** @@ -195,42 +195,6 @@ export class RedBlackTree< return keyNodeEntryOrRaw instanceof RedBlackTreeNode; } - // /** - // * Time Complexity: O(1) - // * Space Complexity: O(1) - // */ - // - // /** - // * Time Complexity: O(1) - // * Space Complexity: O(1) - // * - // * The function `keyValueNodeEntryRawToNodeAndValue` takes a key, value, or entry and returns a node if it is - // * valid, otherwise it returns undefined. - // * @param {BTNRep} keyNodeEntryOrRaw - The key, value, or entry to convert. - // * @param {V} [value] - The value associated with the key (if `keyNodeEntryOrRaw` is a key). - // * @returns {NODE | undefined} - The corresponding Red-Black Tree node, or `undefined` if conversion fails. - // */ - // override keyValueNodeEntryRawToNodeAndValue(keyNodeEntryOrRaw: BTNRep | R, value?: V): NODE | undefined { - // - // if (keyNodeEntryOrRaw === null || keyNodeEntryOrRaw === undefined) return; - // if (this.isNode(keyNodeEntryOrRaw)) return keyNodeEntryOrRaw; - // - // if (this._toEntryFn) { - // const [key, entryValue] = this._toEntryFn(keyNodeEntryOrRaw as R); - // if (this.isKey(key)) return this.createNode(key, value ?? entryValue, 'RED'); - // } - // - // if (this.isEntry(keyNodeEntryOrRaw)) { - // const [key, value] = keyNodeEntryOrRaw; - // if (key === undefined || key === null) return; - // else return this.createNode(key, value, 'RED'); - // } - // - // if (this.isKey(keyNodeEntryOrRaw)) return this.createNode(keyNodeEntryOrRaw, value, 'RED'); - // - // return ; - // } - /** * Time Complexity: O(1) * Space Complexity: O(1) @@ -356,6 +320,40 @@ export class RedBlackTree< return results; } + /** + * Time Complexity: O(n) + * Space Complexity: O(n) + * + * The `map` function in TypeScript overrides the default behavior to create a new Red-Black Tree by + * applying a callback to each entry in the original tree. + * @param callback - A function that will be called for each entry in the tree, with parameters + * representing the key, value, index, and the tree itself. It should return an entry for the new + * tree. + * @param [options] - The `options` parameter in the `map` method is of type `RedBlackTreeOptions`. This parameter allows you to specify additional options or configurations for the Red-Black + * Tree that will be created during the mapping process. These options could include things like + * custom comparators + * @param {any} [thisArg] - The `thisArg` parameter in the `override map` function is used to specify + * the value of `this` when executing the `callback` function. It allows you to set the context + * (value of `this`) for the callback function. This can be useful when you want to access properties + * or + * @returns A new Red-Black Tree is being returned, where each entry has been transformed using the + * provided callback function. + */ + // @ts-ignore + override map( + callback: EntryCallback, + options?: RedBlackTreeOptions, + thisArg?: any + ) { + const newTree = new RedBlackTree([], options); + let index = 0; + for (const [key, value] of this) { + newTree.add(callback.call(thisArg, key, value, index++, this)); + } + return newTree; + } + /** * Time Complexity: O(1) * Space Complexity: O(1) @@ -422,7 +420,7 @@ export class RedBlackTree< if (!parent) { this._setRoot(node); - } else if (node.key < parent.key) { + } else if (this._compare(node.key, parent.key) < 0) { parent.left = node; } else { parent.right = node; diff --git a/src/data-structures/binary-tree/tree-multi-map.ts b/src/data-structures/binary-tree/tree-multi-map.ts index 77d1865..7099311 100644 --- a/src/data-structures/binary-tree/tree-multi-map.ts +++ b/src/data-structures/binary-tree/tree-multi-map.ts @@ -9,10 +9,10 @@ import type { BinaryTreeDeleteResult, BSTNOptKeyOrNode, BTNRep, + EntryCallback, IterationType, OptNode, RBTNColor, - TreeMultiMapNested, TreeMultiMapNodeNested, TreeMultiMapOptions } from '../../types'; @@ -65,11 +65,10 @@ export class TreeMultiMap< K = any, V = any, R = object, - NODE extends TreeMultiMapNode = TreeMultiMapNode>, - TREE extends TreeMultiMap = TreeMultiMap> + NODE extends TreeMultiMapNode = TreeMultiMapNode> > - extends RedBlackTree - implements IBinaryTree + extends RedBlackTree + implements IBinaryTree { /** * The constructor function initializes a TreeMultiMap object with optional initial data. @@ -135,53 +134,15 @@ export class TreeMultiMap< * @returns a new instance of the `TreeMultiMap` class, with the provided options merged with the * existing `iterationType` property. The returned value is casted as `TREE`. */ - override createTree(options?: TreeMultiMapOptions): TREE { - return new TreeMultiMap([], { + // @ts-ignore + override createTree(options?: TreeMultiMapOptions) { + return new TreeMultiMap([], { iterationType: this.iterationType, isMapMode: this._isMapMode, - extractComparable: this._extractComparable, + specifyComparable: this._specifyComparable, toEntryFn: this._toEntryFn, ...options - }) as TREE; - } - - /** - * The function `keyValueNodeEntryRawToNodeAndValue` takes in a key, value, and count and returns a - * node based on the input. - * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter - * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep`. - * @param {V} [value] - The `value` parameter is an optional value that represents the value - * associated with the key in the node. It is used when creating a new node or updating the value of - * an existing node. - * @param [count=1] - The `count` parameter is an optional parameter that specifies the number of - * 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( - keyNodeEntryOrRaw: BTNRep | R, - value?: V, - count = 1 - ): [NODE | undefined, V | undefined] { - if (keyNodeEntryOrRaw === undefined || keyNodeEntryOrRaw === null) return [undefined, undefined]; - - if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; - - if (this.isEntry(keyNodeEntryOrRaw)) { - const [key, entryValue] = keyNodeEntryOrRaw; - if (key === undefined || key === null) return [undefined, undefined]; - const finalValue = value ?? entryValue; - if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue]; - } - - if (this.isRaw(keyNodeEntryOrRaw)) { - const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw); - const finalValue = value ?? entryValue; - if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue]; - } - - if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value, 'BLACK', count), value]; - - return [undefined, undefined]; + }); } /** @@ -402,13 +363,82 @@ export class TreeMultiMap< * The function overrides the clone method to create a deep copy of a tree object. * @returns The `clone()` method is returning a cloned instance of the `TREE` object. */ - override clone(): TREE { + // @ts-ignore + override clone() { const cloned = this.createTree(); this.bfs(node => cloned.add(node.key, undefined, node.count)); if (this._isMapMode) cloned._store = this._store; return cloned; } + /** + * The `map` function in TypeScript overrides the default behavior to create a new TreeMultiMap with + * modified entries based on a provided callback. + * @param callback - The `callback` parameter is a function that will be called for each entry in the + * map. It takes four arguments: + * @param [options] - The `options` parameter in the `override map` function is of type + * `TreeMultiMapOptions`. This parameter allows you to provide additional configuration + * options when creating a new `TreeMultiMap` instance within the `map` function. These options could + * include things like + * @param {any} [thisArg] - The `thisArg` parameter in the `override map` function is used to specify + * the value of `this` when executing the `callback` function. It allows you to set the context + * (value of `this`) for the callback function when it is called within the `map` function. This + * @returns A new TreeMultiMap instance is being returned, which is populated with entries generated + * by the provided callback function. + */ + // @ts-ignore + override map( + callback: EntryCallback, + options?: TreeMultiMapOptions, + thisArg?: any + ) { + const newTree = new TreeMultiMap([], options); + let index = 0; + for (const [key, value] of this) { + newTree.add(callback.call(thisArg, key, value, index++, this)); + } + return newTree; + } + + /** + * The function `keyValueNodeEntryRawToNodeAndValue` takes in a key, value, and count and returns a + * node based on the input. + * @param {BTNRep | R} keyNodeEntryOrRaw - The parameter + * `keyNodeEntryOrRaw` can be of type `R` or `BTNRep`. + * @param {V} [value] - The `value` parameter is an optional value that represents the value + * associated with the key in the node. It is used when creating a new node or updating the value of + * an existing node. + * @param [count=1] - The `count` parameter is an optional parameter that specifies the number of + * 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( + keyNodeEntryOrRaw: BTNRep | R, + value?: V, + count = 1 + ): [NODE | undefined, V | undefined] { + if (keyNodeEntryOrRaw === undefined || keyNodeEntryOrRaw === null) return [undefined, undefined]; + + if (this.isNode(keyNodeEntryOrRaw)) return [keyNodeEntryOrRaw, value]; + + if (this.isEntry(keyNodeEntryOrRaw)) { + const [key, entryValue] = keyNodeEntryOrRaw; + if (key === undefined || key === null) return [undefined, undefined]; + const finalValue = value ?? entryValue; + if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue]; + } + + if (this.isRaw(keyNodeEntryOrRaw)) { + const [key, entryValue] = this._toEntryFn!(keyNodeEntryOrRaw); + const finalValue = value ?? entryValue; + if (this.isKey(key)) return [this.createNode(key, finalValue, 'BLACK', count), finalValue]; + } + + if (this.isKey(keyNodeEntryOrRaw)) return [this.createNode(keyNodeEntryOrRaw, value, 'BLACK', count), value]; + + return [undefined, undefined]; + } + /** * Time Complexity: O(1) * Space Complexity: O(1) diff --git a/src/data-structures/graph/abstract-graph.ts b/src/data-structures/graph/abstract-graph.ts index 542ea06..4e856e5 100644 --- a/src/data-structures/graph/abstract-graph.ts +++ b/src/data-structures/graph/abstract-graph.ts @@ -947,7 +947,7 @@ export abstract class AbstractGraph< const filtered: [VertexKey, V | undefined][] = []; let index = 0; for (const [key, value] of this) { - if (predicate.call(thisArg, value, key, index, this)) { + if (predicate.call(thisArg, key, value, index, this)) { filtered.push([key, value]); } index++; @@ -972,7 +972,7 @@ export abstract class AbstractGraph< const mapped: T[] = []; let index = 0; for (const [key, value] of this) { - mapped.push(callback.call(thisArg, value, key, index, this)); + mapped.push(callback.call(thisArg, key, value, index, this)); index++; } return mapped; diff --git a/src/data-structures/linked-list/doubly-linked-list.ts b/src/data-structures/linked-list/doubly-linked-list.ts index 2dc15e2..62a4620 100644 --- a/src/data-structures/linked-list/doubly-linked-list.ts +++ b/src/data-structures/linked-list/doubly-linked-list.ts @@ -588,6 +588,19 @@ export class DoublyLinkedList extends IterableElementBase(data: E[]) { + return new DoublyLinkedList(data); + } + /** * Time Complexity: O(1) * Space Complexity: O(1) @@ -1252,19 +1265,6 @@ export class DoublyLinkedList extends IterableElementBase(data: E[]) { - return new DoublyLinkedList(data); - } - /** * The function returns an iterator that iterates over the values of a linked list. */ diff --git a/src/data-structures/linked-list/singly-linked-list.ts b/src/data-structures/linked-list/singly-linked-list.ts index 5776faf..6197f46 100644 --- a/src/data-structures/linked-list/singly-linked-list.ts +++ b/src/data-structures/linked-list/singly-linked-list.ts @@ -116,6 +116,23 @@ export class SinglyLinkedList extends IterableElementBase(data: E[]) { + const singlyLinkedList = new SinglyLinkedList(); + for (const item of data) { + singlyLinkedList.push(item); + } + return singlyLinkedList; + } + /** * Time Complexity: O(1) * Space Complexity: O(1) @@ -763,23 +780,6 @@ export class SinglyLinkedList extends IterableElementBase(data: E[]) { - const singlyLinkedList = new SinglyLinkedList(); - for (const item of data) { - singlyLinkedList.push(item); - } - return singlyLinkedList; - } - /** * The _isPredicate function in TypeScript checks if the input is a function that takes a * SinglyLinkedListNode as an argument and returns a boolean. diff --git a/src/interfaces/binary-tree.ts b/src/interfaces/binary-tree.ts index 7c9678e..d45e87c 100644 --- a/src/interfaces/binary-tree.ts +++ b/src/interfaces/binary-tree.ts @@ -1,24 +1,14 @@ -import { BinaryTree, BinaryTreeNode } from '../data-structures'; -import type { - BinaryTreeDeleteResult, - BinaryTreeNested, - BinaryTreeNodeNested, - BinaryTreeOptions, - BTNRep, - NodePredicate -} from '../types'; +import { BinaryTreeNode } from '../data-structures'; +import type { BinaryTreeDeleteResult, BinaryTreeNodeNested, BTNRep, NodePredicate } from '../types'; export interface IBinaryTree< K = any, V = any, R = object, - NODE extends BinaryTreeNode = BinaryTreeNodeNested, - TREE extends BinaryTree = BinaryTreeNested + NODE extends BinaryTreeNode = BinaryTreeNodeNested > { createNode(key: K, value?: NODE['value']): NODE; - createTree(options?: Partial>): TREE; - add(keyOrNodeOrEntryOrRawElement: BTNRep, value?: V, count?: number): boolean; addMany(nodes: Iterable>, values?: Iterable): boolean[]; diff --git a/src/types/data-structures/base/base.ts b/src/types/data-structures/base/base.ts index 17895b6..b816749 100644 --- a/src/types/data-structures/base/base.ts +++ b/src/types/data-structures/base/base.ts @@ -1,6 +1,6 @@ import { IterableElementBase, IterableEntryBase } from '../../../data-structures'; -export type EntryCallback = (value: V, key: K, index: number, container: IterableEntryBase) => R; +export type EntryCallback = (key: K, value: V, index: number, container: IterableEntryBase) => R; export type ElementCallback = (element: E, index: number, container: IterableElementBase) => RT; export type ReduceEntryCallback = ( accumulator: R, diff --git a/src/types/data-structures/binary-tree/avl-tree-multi-map.ts b/src/types/data-structures/binary-tree/avl-tree-multi-map.ts index 0f418b0..1e6ab4a 100644 --- a/src/types/data-structures/binary-tree/avl-tree-multi-map.ts +++ b/src/types/data-structures/binary-tree/avl-tree-multi-map.ts @@ -1,8 +1,6 @@ -import { AVLTreeMultiMap, AVLTreeMultiMapNode } from '../../../data-structures'; +import { AVLTreeMultiMapNode } from '../../../data-structures'; import type { AVLTreeOptions } from './avl-tree'; -export type AVLTreeMultiMapNodeNested = AVLTreeMultiMapNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - -export type AVLTreeMultiMapNested> = AVLTreeMultiMap>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type AVLTreeMultiMapNodeNested = AVLTreeMultiMapNode>>>>>>>>> export type AVLTreeMultiMapOptions = AVLTreeOptions & {} diff --git a/src/types/data-structures/binary-tree/avl-tree.ts b/src/types/data-structures/binary-tree/avl-tree.ts index b84b3b6..c8f0b54 100644 --- a/src/types/data-structures/binary-tree/avl-tree.ts +++ b/src/types/data-structures/binary-tree/avl-tree.ts @@ -1,8 +1,6 @@ -import { AVLTree, AVLTreeNode } from '../../../data-structures'; +import { AVLTreeNode } from '../../../data-structures'; import { BSTOptions } from './bst'; -export type AVLTreeNodeNested = AVLTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - -export type AVLTreeNested> = AVLTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type AVLTreeNodeNested = AVLTreeNode>>>>>>>>> export type AVLTreeOptions = BSTOptions & {}; diff --git a/src/types/data-structures/binary-tree/binary-tree.ts b/src/types/data-structures/binary-tree/binary-tree.ts index 1dc9ea7..cdd4a08 100644 --- a/src/types/data-structures/binary-tree/binary-tree.ts +++ b/src/types/data-structures/binary-tree/binary-tree.ts @@ -1,10 +1,10 @@ -import { BinaryTree, BinaryTreeNode } from '../../../data-structures'; +import { BinaryTreeNode } from '../../../data-structures'; import { IterationType, OptValue } from '../../common'; import { DFSOperation } from '../../../common'; -export type BinaryTreeNodeNested = BinaryTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type BinaryTreeNodeNested = BinaryTreeNode>>>>>>>>> -export type BinaryTreeNested> = BinaryTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +// export type BinaryTreeNested> = BinaryTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> export type ToEntryFn = (rawElement: R) => BTNEntry; diff --git a/src/types/data-structures/binary-tree/bst.ts b/src/types/data-structures/binary-tree/bst.ts index 453b460..ccc3042 100644 --- a/src/types/data-structures/binary-tree/bst.ts +++ b/src/types/data-structures/binary-tree/bst.ts @@ -1,13 +1,11 @@ -import { BST, BSTNode } from '../../../data-structures'; +import { BSTNode } from '../../../data-structures'; import type { BinaryTreeOptions } from './binary-tree'; import { Comparable } from '../../utils'; -export type BSTNodeNested = BSTNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - -export type BSTNested> = BST>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type BSTNodeNested = BSTNode>>>>>>>>> export type BSTOptions = BinaryTreeOptions & { - extractComparable?: (key: K) => Comparable + specifyComparable?: (key: K) => Comparable isReverse?: boolean; } diff --git a/src/types/data-structures/binary-tree/rb-tree.ts b/src/types/data-structures/binary-tree/rb-tree.ts index ee415a7..743fe3a 100644 --- a/src/types/data-structures/binary-tree/rb-tree.ts +++ b/src/types/data-structures/binary-tree/rb-tree.ts @@ -1,10 +1,8 @@ -import { RedBlackTree, RedBlackTreeNode } from '../../../data-structures'; -import type { BSTOptions } from "./bst"; +import { RedBlackTreeNode } from '../../../data-structures'; +import type { BSTOptions } from './bst'; export type RBTNColor = 'RED' | 'BLACK'; -export type RedBlackTreeNodeNested = RedBlackTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type RedBlackTreeNodeNested = RedBlackTreeNode>>>>>>>>> -export type RedBlackTreeNested> = RedBlackTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - -export type RBTreeOptions = Omit, 'isReverse'> & {}; +export type RedBlackTreeOptions = Omit, 'isReverse'> & {}; diff --git a/src/types/data-structures/binary-tree/tree-multi-map.ts b/src/types/data-structures/binary-tree/tree-multi-map.ts index 43ccc38..ed7918c 100644 --- a/src/types/data-structures/binary-tree/tree-multi-map.ts +++ b/src/types/data-structures/binary-tree/tree-multi-map.ts @@ -1,8 +1,6 @@ -import { TreeMultiMap, TreeMultiMapNode } from '../../../data-structures'; -import type { RBTreeOptions } from './rb-tree'; +import { TreeMultiMapNode } from '../../../data-structures'; +import type { RedBlackTreeOptions } from './rb-tree'; -export type TreeMultiMapNodeNested = TreeMultiMapNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type TreeMultiMapNodeNested = TreeMultiMapNode>>>>>>>>> -export type TreeMultiMapNested> = TreeMultiMap>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> - -export type TreeMultiMapOptions = RBTreeOptions & {} +export type TreeMultiMapOptions = RedBlackTreeOptions & {} diff --git a/test/integration/index.html b/test/integration/index.html index d1fbdf4..fe7c6be 100644 --- a/test/integration/index.html +++ b/test/integration/index.html @@ -102,7 +102,7 @@ try { const { OrderedMap } = sdsl; const { RedBlackTree } = dataStructureTyped; - const cRBTree = new OrderedMap(); + const cRedBlackTree = new OrderedMap(); const rbTree = new RedBlackTree(); const tS = performance.now(); const n = 100000; @@ -117,10 +117,10 @@ rbTree.print(rbTree.root, { isShowRedBlackNIL: true }); const cS = performance.now(); for (let i = 1; i < 100000; i++) { - cRBTree.setElement(i, i); + cRedBlackTree.setElement(i, i); } console.log((performance.now() - cS).toFixed(2), `CRedBlackTree ${n.toLocaleString()} add`); - console.log(cRBTree.size(), `cRBTree.size()`); + console.log(cRedBlackTree.size(), `cRedBlackTree.size()`); } catch (e) { console.error(e); } diff --git a/test/performance/data-structures/binary-tree/binary-tree-overall.test.ts b/test/performance/data-structures/binary-tree/binary-tree-overall.test.ts index ad115fa..d90b019 100644 --- a/test/performance/data-structures/binary-tree/binary-tree-overall.test.ts +++ b/test/performance/data-structures/binary-tree/binary-tree-overall.test.ts @@ -9,14 +9,14 @@ const { TEN_THOUSAND } = magnitude; const arr = getRandomIntArray(TEN_THOUSAND, 0, TEN_THOUSAND - 1, true); suite - .add(`${TEN_THOUSAND.toLocaleString()} RBTree add randomly`, () => { + .add(`${TEN_THOUSAND.toLocaleString()} RedBlackTree add randomly`, () => { rbTree.clear(); for (let i = 0; i < arr.length; i++) rbTree.add(arr[i]); }) - .add(`${TEN_THOUSAND.toLocaleString()} RBTree get randomly`, () => { + .add(`${TEN_THOUSAND.toLocaleString()} RedBlackTree get randomly`, () => { for (let i = 0; i < arr.length; i++) rbTree.get(arr[i]); }) - .add(`${TEN_THOUSAND.toLocaleString()} RBTree add & delete randomly`, () => { + .add(`${TEN_THOUSAND.toLocaleString()} RedBlackTree add & delete randomly`, () => { rbTree.clear(); for (let i = 0; i < arr.length; i++) rbTree.add(arr[i]); for (let i = 0; i < arr.length; i++) rbTree.delete(arr[i]); diff --git a/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts b/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts index 65070b5..d2128b7 100644 --- a/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts +++ b/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts @@ -637,7 +637,7 @@ describe('AVLTreeMultiMap iterative methods test', () => { }); it('filter should return a new tree with filtered elements', () => { - const filteredTree = treeMM.filter((value, key) => key > 1); + const filteredTree = treeMM.filter(key => key > 1); expect(filteredTree.size).toBe(2); expect([...filteredTree]).toEqual([ [2, 'b'], @@ -646,12 +646,12 @@ describe('AVLTreeMultiMap iterative methods test', () => { }); it('map should return a new tree with modified elements', () => { - const mappedTree = treeMM.map((value, key) => (key * 2).toString()); + const mappedTree = treeMM.map((key, value) => [(key * 2).toString(), value]); expect(mappedTree.size).toBe(3); expect([...mappedTree]).toEqual([ - [1, '2'], - [2, '4'], - [3, '6'] + ['2', 'a'], + ['4', 'b'], + ['6', 'c'] ]); }); @@ -736,7 +736,7 @@ describe('AVLTree toEntryFn', () => { { obj: { id: 5 } } ]) ).toThrowError( - `When comparing object types, a custom extractComparable must be defined in the constructor's options parameter.` + `When comparing object types, a custom specifyComparable must be defined in the constructor's options parameter.` ); }); @@ -744,7 +744,7 @@ describe('AVLTree toEntryFn', () => { const tree = new AVLTreeMultiMap<{ obj: { id: number } }, number>( [{ obj: { id: 1 } }, { obj: { id: 2 } }, { obj: { id: 3 } }, { obj: { id: 4 } }, { obj: { id: 5 } }], { - extractComparable: key => key.obj.id + specifyComparable: key => key.obj.id } ); diff --git a/test/unit/data-structures/binary-tree/avl-tree.test.ts b/test/unit/data-structures/binary-tree/avl-tree.test.ts index a3499e5..59d09ef 100644 --- a/test/unit/data-structures/binary-tree/avl-tree.test.ts +++ b/test/unit/data-structures/binary-tree/avl-tree.test.ts @@ -397,7 +397,7 @@ describe('AVLTree iterative methods test', () => { }); it('filter should return a new tree with filtered elements', () => { - const filteredTree = avl.filter((value, key) => key > 1); + const filteredTree = avl.filter(key => key > 1); expect(filteredTree.size).toBe(2); expect([...filteredTree]).toEqual([ [2, 'b'], @@ -406,12 +406,12 @@ describe('AVLTree iterative methods test', () => { }); it('map should return a new tree with modified elements', () => { - const mappedTree = avl.map((value, key) => (key * 2).toString()); + const mappedTree = avl.map((key, value) => [(key * 2).toString(), value]); expect(mappedTree.size).toBe(3); expect([...mappedTree]).toEqual([ - [1, '2'], - [2, '4'], - [3, '6'] + ['2', 'a'], + ['4', 'b'], + ['6', 'c'] ]); }); diff --git a/test/unit/data-structures/binary-tree/binary-tree.test.ts b/test/unit/data-structures/binary-tree/binary-tree.test.ts index 5881d08..3dd2a4f 100644 --- a/test/unit/data-structures/binary-tree/binary-tree.test.ts +++ b/test/unit/data-structures/binary-tree/binary-tree.test.ts @@ -1353,7 +1353,7 @@ describe('BinaryTree iterative methods test', () => { }); it('filter should return a new tree with filtered elements', () => { - const filteredTree = binaryTree.filter((value, key) => key > 1); + const filteredTree = binaryTree.filter((key, value) => key > 1); expect(filteredTree.size).toBe(2); expect([...filteredTree]).toEqual([ [3, 'c'], @@ -1362,12 +1362,12 @@ describe('BinaryTree iterative methods test', () => { }); it('map should return a new tree with modified elements', () => { - const mappedTree = binaryTree.map((value, key) => (key * 2).toString()); + const mappedTree = binaryTree.map((key, value) => [(key * 2).toString(), value]); expect(mappedTree.size).toBe(3); expect([...mappedTree]).toEqual([ - [1, '2'], - [2, '4'], - [3, '6'] + ['2', 'a'], + ['4', 'b'], + ['6', 'c'] ]); }); diff --git a/test/unit/data-structures/binary-tree/bst.test.ts b/test/unit/data-structures/binary-tree/bst.test.ts index 0f72ef1..54aa66e 100644 --- a/test/unit/data-structures/binary-tree/bst.test.ts +++ b/test/unit/data-structures/binary-tree/bst.test.ts @@ -981,7 +981,7 @@ describe('BST operations test recursively', () => { if (isTestStackOverflow) { it('should getLeftMost', () => { - const bst = new BST([], { extractComparable: key => key }); + const bst = new BST([], { specifyComparable: key => key }); for (let i = 1; i <= SYSTEM_MAX_CALL_STACK; i++) bst.add(i); expect(() => { @@ -1121,7 +1121,7 @@ describe('BST iterative methods test', () => { }); it('filter should return a new tree with filtered elements', () => { - const filteredTree = bst.filter((value, key) => key > 1); + const filteredTree = bst.filter((key, value) => key > 1); expect(filteredTree.size).toBe(2); expect([...filteredTree]).toEqual([ [2, 'b'], @@ -1130,12 +1130,12 @@ describe('BST iterative methods test', () => { }); it('map should return a new tree with modified elements', () => { - const mappedTree = bst.map((value, key) => (key * 2).toString()); + const mappedTree = bst.map((key, value) => [(key * 2).toString(), value]); expect(mappedTree.size).toBe(3); expect([...mappedTree]).toEqual([ - [1, '2'], - [2, '4'], - [3, '6'] + ['2', 'a'], + ['4', 'b'], + ['6', 'c'] ]); }); diff --git a/test/unit/data-structures/binary-tree/data/cost-of-living-by-country.ts b/test/unit/data-structures/binary-tree/data/cost-of-living-by-country.ts new file mode 100644 index 0000000..e5ceac4 --- /dev/null +++ b/test/unit/data-structures/binary-tree/data/cost-of-living-by-country.ts @@ -0,0 +1,259 @@ +export type CostOfLiving = { + rank: number; + country: string; + 'Meal, Inexpensive Restaurant': number; + 'Domestic Beer (0.5 liter draught)': number; + 'Water (0.33 liter bottle)': number; + 'Milk (regular), (1 liter)': number; + 'Loaf of Fresh White Bread (500g)': number; + 'Eggs (regular) (12)': number; + 'Local Cheese (1kg)': number; + 'Water (1.5 liter bottle)': number; + 'One-way Ticket (Local Transport)': number; + 'Gasoline (1 liter)': number; + 'Apartment (3 bedrooms) in City Centre': number; + 'Basic (Electricity, Heating, Cooling, Water, Garbage) for 85m2 Apartment': number; + 'Internet (60 Mbps or More, Unlimited Data, Cable/ADSL)': number; + 'Mobile Phone Monthly Plan with Calls and 10GB+ Data': number; + 'Cinema, International Release, 1 Seat': number; + '1 Pair of Jeans (Levis 501 Or Similar)': number; + 'Price per Square Meter to Buy Apartment in City Centre': number; + 'Average Monthly Net Salary (After Tax)': number; + 'Mortgage Interest Rate in Percentages (%), Yearly, for 20 Years Fixed-Rate': number; + 'Taxi 1km (Normal Tariff)': number; + 'Oranges (1kg)': number; + 'Potato (1kg)': number; + 'Lettuce (1 head)': number; + 'Rice (white), (1kg)': number; + 'Onion (1kg)': number; + 'Beef Round (1kg) (or Equivalent Back Leg Red Meat)': number; + 'Toyota Corolla Sedan 1.6l 97kW Comfort (Or Equivalent New Car)': number; + 'Preschool (or Kindergarten), Full Day, Private, Monthly for 1 Child': number; +}; + +export const costOfLiving: CostOfLiving[] = [ + { + rank: 1, + country: 'Switzerland', + 'Meal, Inexpensive Restaurant': 27.98, + 'Domestic Beer (0.5 liter draught)': 7.83, + 'Water (0.33 liter bottle)': 4.49, + 'Milk (regular), (1 liter)': 1.95, + 'Loaf of Fresh White Bread (500g)': 3.63, + 'Eggs (regular) (12)': 6.83, + 'Local Cheese (1kg)': 26.02, + 'Water (1.5 liter bottle)': 1.13, + 'One-way Ticket (Local Transport)': 3.92, + 'Gasoline (1 liter)': 2.06, + 'Apartment (3 bedrooms) in City Centre': 3359.12, + 'Basic (Electricity, Heating, Cooling, Water, Garbage) for 85m2 Apartment': 243.82, + 'Internet (60 Mbps or More, Unlimited Data, Cable/ADSL)': 54.3, + 'Mobile Phone Monthly Plan with Calls and 10GB+ Data': 51.26, + 'Cinema, International Release, 1 Seat': 22.38, + '1 Pair of Jeans (Levis 501 Or Similar)': 120.18, + 'Price per Square Meter to Buy Apartment in City Centre': 16439.97, + 'Average Monthly Net Salary (After Tax)': 6628.35, + 'Mortgage Interest Rate in Percentages (%), Yearly, for 20 Years Fixed-Rate': 2.49, + 'Taxi 1km (Normal Tariff)': 4.36, + 'Oranges (1kg)': 2.93, + 'Potato (1kg)': 2.48, + 'Lettuce (1 head)': 2.35, + 'Rice (white), (1kg)': 3.98, + 'Onion (1kg)': 2.29, + 'Beef Round (1kg) (or Equivalent Back Leg Red Meat)': 45.89, + 'Toyota Corolla Sedan 1.6l 97kW Comfort (Or Equivalent New Car)': 37549.18, + 'Preschool (or Kindergarten), Full Day, Private, Monthly for 1 Child': 2832.82 + }, + { + rank: 23, + country: 'New Zealand', + 'Meal, Inexpensive Restaurant': 14.58, + 'Domestic Beer (0.5 liter draught)': 6.42, + 'Water (0.33 liter bottle)': 2.01, + 'Milk (regular), (1 liter)': 1.76, + 'Loaf of Fresh White Bread (500g)': 1.87, + 'Eggs (regular) (12)': 6.32, + 'Local Cheese (1kg)': 8.02, + 'Water (1.5 liter bottle)': 1.16, + 'One-way Ticket (Local Transport)': 2.33, + 'Gasoline (1 liter)': 1.63, + 'Apartment (3 bedrooms) in City Centre': 1758.7, + 'Basic (Electricity, Heating, Cooling, Water, Garbage) for 85m2 Apartment': 135.28, + 'Internet (60 Mbps or More, Unlimited Data, Cable/ADSL)': 50.44, + 'Mobile Phone Monthly Plan with Calls and 10GB+ Data': 34.27, + 'Cinema, International Release, 1 Seat': 11.67, + '1 Pair of Jeans (Levis 501 Or Similar)': 67.67, + 'Price per Square Meter to Buy Apartment in City Centre': 5371.0, + 'Average Monthly Net Salary (After Tax)': 2920.79, + 'Mortgage Interest Rate in Percentages (%), Yearly, for 20 Years Fixed-Rate': 6.84, + 'Taxi 1km (Normal Tariff)': 2.01, + 'Oranges (1kg)': 2.94, + 'Potato (1kg)': 2.07, + 'Lettuce (1 head)': 2.35, + 'Rice (white), (1kg)': 1.82, + 'Onion (1kg)': 1.77, + 'Beef Round (1kg) (or Equivalent Back Leg Red Meat)': 12.58, + 'Toyota Corolla Sedan 1.6l 97kW Comfort (Or Equivalent New Car)': 20980.7, + 'Preschool (or Kindergarten), Full Day, Private, Monthly for 1 Child': 752.33 + }, + { + rank: 41, + country: 'Mexico', + 'Meal, Inexpensive Restaurant': 9.79, + 'Domestic Beer (0.5 liter draught)': 2.2, + 'Water (0.33 liter bottle)': 0.83, + 'Milk (regular), (1 liter)': 1.35, + 'Loaf of Fresh White Bread (500g)': 2.36, + 'Eggs (regular) (12)': 2.16, + 'Local Cheese (1kg)': 7.87, + 'Water (1.5 liter bottle)': 0.91, + 'One-way Ticket (Local Transport)': 0.54, + 'Gasoline (1 liter)': 1.18, + 'Apartment (3 bedrooms) in City Centre': 1148.12, + 'Basic (Electricity, Heating, Cooling, Water, Garbage) for 85m2 Apartment': 59.55, + 'Internet (60 Mbps or More, Unlimited Data, Cable/ADSL)': 25.94, + 'Mobile Phone Monthly Plan with Calls and 10GB+ Data': 21.52, + 'Cinema, International Release, 1 Seat': 4.41, + '1 Pair of Jeans (Levis 501 Or Similar)': 49.23, + 'Price per Square Meter to Buy Apartment in City Centre': 1781.03, + 'Average Monthly Net Salary (After Tax)': 660.35, + 'Mortgage Interest Rate in Percentages (%), Yearly, for 20 Years Fixed-Rate': 11.43, + 'Taxi 1km (Normal Tariff)': 1.91, + 'Oranges (1kg)': 1.61, + 'Potato (1kg)': 1.81, + 'Lettuce (1 head)': 1.17, + 'Rice (white), (1kg)': 1.58, + 'Onion (1kg)': 1.66, + 'Beef Round (1kg) (or Equivalent Back Leg Red Meat)': 9.79, + 'Toyota Corolla Sedan 1.6l 97kW Comfort (Or Equivalent New Car)': 21077.68, + 'Preschool (or Kindergarten), Full Day, Private, Monthly for 1 Child': 224.39 + }, + { + rank: 52, + country: 'South Africa', + 'Meal, Inexpensive Restaurant': 8.28, + 'Domestic Beer (0.5 liter draught)': 2.21, + 'Water (0.33 liter bottle)': 0.73, + 'Milk (regular), (1 liter)': 1.07, + 'Loaf of Fresh White Bread (500g)': 0.97, + 'Eggs (regular) (12)': 2.26, + 'Local Cheese (1kg)': 7.35, + 'Water (1.5 liter bottle)': 0.92, + 'One-way Ticket (Local Transport)': 1.66, + 'Gasoline (1 liter)': 1.29, + 'Apartment (3 bedrooms) in City Centre': 892.16, + 'Basic (Electricity, Heating, Cooling, Water, Garbage) for 85m2 Apartment': 104.63, + 'Internet (60 Mbps or More, Unlimited Data, Cable/ADSL)': 41.21, + 'Mobile Phone Monthly Plan with Calls and 10GB+ Data': 31.79, + 'Cinema, International Release, 1 Seat': 6.62, + '1 Pair of Jeans (Levis 501 Or Similar)': 47.28, + 'Price per Square Meter to Buy Apartment in City Centre': 1031.57, + 'Average Monthly Net Salary (After Tax)': 1383.11, + 'Mortgage Interest Rate in Percentages (%), Yearly, for 20 Years Fixed-Rate': 11.58, + 'Taxi 1km (Normal Tariff)': 1.1, + 'Oranges (1kg)': 1.4, + 'Potato (1kg)': 1.21, + 'Lettuce (1 head)': 1.02, + 'Rice (white), (1kg)': 1.53, + 'Onion (1kg)': 1.34, + 'Beef Round (1kg) (or Equivalent Back Leg Red Meat)': 6.55, + 'Toyota Corolla Sedan 1.6l 97kW Comfort (Or Equivalent New Car)': 23105.93, + 'Preschool (or Kindergarten), Full Day, Private, Monthly for 1 Child': 193.32 + }, + { + rank: 65, + country: 'Japan', + 'Meal, Inexpensive Restaurant': 6.46, + 'Domestic Beer (0.5 liter draught)': 3.23, + 'Water (0.33 liter bottle)': 0.73, + 'Milk (regular), (1 liter)': 1.4, + 'Loaf of Fresh White Bread (500g)': 1.46, + 'Eggs (regular) (12)': 2.08, + 'Local Cheese (1kg)': 11.12, + 'Water (1.5 liter bottle)': 0.85, + 'One-way Ticket (Local Transport)': 1.42, + 'Gasoline (1 liter)': 1.11, + 'Apartment (3 bedrooms) in City Centre': 1248.4, + 'Basic (Electricity, Heating, Cooling, Water, Garbage) for 85m2 Apartment': 166.85, + 'Internet (60 Mbps or More, Unlimited Data, Cable/ADSL)': 32.64, + 'Mobile Phone Monthly Plan with Calls and 10GB+ Data': 25.29, + 'Cinema, International Release, 1 Seat': 12.28, + '1 Pair of Jeans (Levis 501 Or Similar)': 43.21, + 'Price per Square Meter to Buy Apartment in City Centre': 6324.16, + 'Average Monthly Net Salary (After Tax)': 2069.74, + 'Mortgage Interest Rate in Percentages (%), Yearly, for 20 Years Fixed-Rate': 1.83, + 'Taxi 1km (Normal Tariff)': 2.91, + 'Oranges (1kg)': 4.35, + 'Potato (1kg)': 2.63, + 'Lettuce (1 head)': 1.26, + 'Rice (white), (1kg)': 3.72, + 'Onion (1kg)': 2.39, + 'Beef Round (1kg) (or Equivalent Back Leg Red Meat)': 16.87, + 'Toyota Corolla Sedan 1.6l 97kW Comfort (Or Equivalent New Car)': 16534.56, + 'Preschool (or Kindergarten), Full Day, Private, Monthly for 1 Child': 353.95 + }, + { + rank: 72, + country: 'Brazil', + 'Meal, Inexpensive Restaurant': 5.17, + 'Domestic Beer (0.5 liter draught)': 1.72, + 'Water (0.33 liter bottle)': 0.65, + 'Milk (regular), (1 liter)': 0.93, + 'Loaf of Fresh White Bread (500g)': 1.43, + 'Eggs (regular) (12)': 1.92, + 'Local Cheese (1kg)': 8.08, + 'Water (1.5 liter bottle)': 0.63, + 'One-way Ticket (Local Transport)': 0.86, + 'Gasoline (1 liter)': 0.98, + 'Apartment (3 bedrooms) in City Centre': 622.9, + 'Basic (Electricity, Heating, Cooling, Water, Garbage) for 85m2 Apartment': 65.01, + 'Internet (60 Mbps or More, Unlimited Data, Cable/ADSL)': 18.39, + 'Mobile Phone Monthly Plan with Calls and 10GB+ Data': 13.92, + 'Cinema, International Release, 1 Seat': 6.03, + '1 Pair of Jeans (Levis 501 Or Similar)': 46.84, + 'Price per Square Meter to Buy Apartment in City Centre': 1513.42, + 'Average Monthly Net Salary (After Tax)': 417.56, + 'Mortgage Interest Rate in Percentages (%), Yearly, for 20 Years Fixed-Rate': 10.98, + 'Taxi 1km (Normal Tariff)': 0.83, + 'Oranges (1kg)': 1.04, + 'Potato (1kg)': 1.11, + 'Lettuce (1 head)': 0.68, + 'Rice (white), (1kg)': 1.11, + 'Onion (1kg)': 1.06, + 'Beef Round (1kg) (or Equivalent Back Leg Red Meat)': 6.93, + 'Toyota Corolla Sedan 1.6l 97kW Comfort (Or Equivalent New Car)': 26186.46, + 'Preschool (or Kindergarten), Full Day, Private, Monthly for 1 Child': 279.87 + }, + { + rank: 74, + country: 'Taiwan', + 'Meal, Inexpensive Restaurant': 4.6, + 'Domestic Beer (0.5 liter draught)': 1.84, + 'Water (0.33 liter bottle)': 0.65, + 'Milk (regular), (1 liter)': 2.99, + 'Loaf of Fresh White Bread (500g)': 1.84, + 'Eggs (regular) (12)': 2.75, + 'Local Cheese (1kg)': 15.19, + 'Water (1.5 liter bottle)': 1.14, + 'One-way Ticket (Local Transport)': 0.77, + 'Gasoline (1 liter)': 0.95, + 'Apartment (3 bedrooms) in City Centre': 1124.03, + 'Basic (Electricity, Heating, Cooling, Water, Garbage) for 85m2 Apartment': 78.3, + 'Internet (60 Mbps or More, Unlimited Data, Cable/ADSL)': 21.65, + 'Mobile Phone Monthly Plan with Calls and 10GB+ Data': 22.18, + 'Cinema, International Release, 1 Seat': 9.51, + '1 Pair of Jeans (Levis 501 Or Similar)': 63.34, + 'Price per Square Meter to Buy Apartment in City Centre': 8859.94, + 'Average Monthly Net Salary (After Tax)': 1654.66, + 'Mortgage Interest Rate in Percentages (%), Yearly, for 20 Years Fixed-Rate': 2.0, + 'Taxi 1km (Normal Tariff)': 0.77, + 'Oranges (1kg)': 2.64, + 'Potato (1kg)': 2.61, + 'Lettuce (1 head)': 1.82, + 'Rice (white), (1kg)': 2.97, + 'Onion (1kg)': 2.15, + 'Beef Round (1kg) (or Equivalent Back Leg Red Meat)': 18.14, + 'Toyota Corolla Sedan 1.6l 97kW Comfort (Or Equivalent New Car)': 24143.1, + 'Preschool (or Kindergarten), Full Day, Private, Monthly for 1 Child': 508.49 + } +]; diff --git a/test/unit/data-structures/binary-tree/overall.test.ts b/test/unit/data-structures/binary-tree/overall.test.ts index 4f9b858..d3f4014 100644 --- a/test/unit/data-structures/binary-tree/overall.test.ts +++ b/test/unit/data-structures/binary-tree/overall.test.ts @@ -82,7 +82,9 @@ describe('Overall BinaryTree Test', () => { expect(bst.has(9)).toBe(true); expect(bst.has(7)).toBe(false); expect(bst.bfs()).toEqual([6, 9, 3, 1]); + const clonedBST = bst.clone(); + expect(clonedBST.size).toBe(4); expect(clonedBST.root?.key).toBe(6); expect(clonedBST.root?.left?.key).toBe(9); diff --git a/test/unit/data-structures/binary-tree/red-black-tree.test.ts b/test/unit/data-structures/binary-tree/red-black-tree.test.ts index 2ace962..c9247b8 100644 --- a/test/unit/data-structures/binary-tree/red-black-tree.test.ts +++ b/test/unit/data-structures/binary-tree/red-black-tree.test.ts @@ -3,6 +3,7 @@ import { getRandomInt, getRandomIntArray, magnitude } from '../../../utils'; import { OrderedMap } from 'js-sdsl'; import { isDebugTest } from '../../../config'; +import { costOfLiving } from './data/cost-of-living-by-country'; const isDebug = isDebugTest; // const isDebug = true; @@ -658,7 +659,7 @@ describe('RedBlackTree 2', () => { }); it('filter should return a new rbTree with filtered elements', () => { - const filteredTree = rbTree.filter((value, key) => key > 1); + const filteredTree = rbTree.filter(key => key > 1); expect(filteredTree.size).toBe(2); expect([...filteredTree]).toEqual([ [2, 'b'], @@ -667,12 +668,12 @@ describe('RedBlackTree 2', () => { }); it('map should return a new rbTree with modified elements', () => { - const mappedTree = rbTree.map((value, key) => (key * 2).toString()); + const mappedTree = rbTree.map((key, value) => [(key * 2).toString(), value]); expect(mappedTree.size).toBe(3); expect([...mappedTree]).toEqual([ - [1, '2'], - [2, '4'], - [3, '6'] + ['2', 'a'], + ['4', 'b'], + ['6', 'c'] ]); }); @@ -820,6 +821,25 @@ describe('RedBlackTree - _deleteFixup', () => { }); }); +describe('real world data', () => { + it('cost of living', () => { + const indexedByRank = new RedBlackTree(costOfLiving, { + specifyComparable: node => node.rank, + toEntryFn: raw => [raw, undefined] + }); + expect(indexedByRank.size).toBe(7); + expect(indexedByRank.dfs(node => node?.key?.country)).toEqual([ + 'Switzerland', + 'New Zealand', + 'Mexico', + 'South Africa', + 'Japan', + 'Brazil', + 'Taiwan' + ]); + }); +}); + describe('classic use', () => { // Test case for finding elements in a given range it('@example Find elements in a range', () => { diff --git a/test/unit/data-structures/binary-tree/tree-multi-map.test.ts b/test/unit/data-structures/binary-tree/tree-multi-map.test.ts index bb99b2d..b0b5693 100644 --- a/test/unit/data-structures/binary-tree/tree-multi-map.test.ts +++ b/test/unit/data-structures/binary-tree/tree-multi-map.test.ts @@ -775,7 +775,7 @@ describe('TreeMultiMap iterative methods test', () => { }); it('filter should return a new tree with filtered elements', () => { - const filteredTree = treeMM.filter((value, key) => key > 1); + const filteredTree = treeMM.filter(key => key > 1); expect(filteredTree.size).toBe(2); expect([...filteredTree]).toEqual([ [2, 'b'], @@ -784,12 +784,12 @@ describe('TreeMultiMap iterative methods test', () => { }); it('map should return a new tree with modified elements', () => { - const mappedTree = treeMM.map((value, key) => (key * 2).toString()); + const mappedTree = treeMM.map((key, value) => [(key * 2).toString(), value]); expect(mappedTree.size).toBe(3); expect([...mappedTree]).toEqual([ - [1, '2'], - [2, '4'], - [3, '6'] + ['2', 'a'], + ['4', 'b'], + ['6', 'c'] ]); }); diff --git a/test/unit/data-structures/graph/directed-graph.test.ts b/test/unit/data-structures/graph/directed-graph.test.ts index f59f548..44c5f51 100644 --- a/test/unit/data-structures/graph/directed-graph.test.ts +++ b/test/unit/data-structures/graph/directed-graph.test.ts @@ -685,7 +685,7 @@ describe('DirectedGraph iterative Methods', () => { }); it('filter should return vertexMap that satisfy the condition', () => { - const filtered = graph.filter((value, vertex) => vertex === 'A' || vertex === 'B'); + const filtered = graph.filter(vertex => vertex === 'A' || vertex === 'B'); expect(filtered).toEqual([ ['A', undefined], ['B', undefined] @@ -693,8 +693,8 @@ describe('DirectedGraph iterative Methods', () => { }); it('map should apply a function to each vertex and return a new array', () => { - const mapped = graph.map((value, vertex) => vertex + '_mapped'); - expect(mapped).toEqual(vertexMap.map(v => v + '_mapped')); + const mapped = graph.map(vertex => vertex + '_mapped'); + expect(mapped).toEqual(vertexMap.map(key => key + '_mapped')); }); it('reduce should accumulate a value based on each vertex', () => {