From 1303aba070a69e15d95aa228f34c12884e67f061 Mon Sep 17 00:00:00 2001 From: Revone Date: Wed, 24 Jan 2024 18:38:34 +0800 Subject: [PATCH] refactor: Use Comparable type constraint for the key in the binary tree to improve comparator performance. --- .../binary-tree/avl-tree-multi-map.ts | 5 +- src/data-structures/binary-tree/avl-tree.ts | 27 +++---- .../binary-tree/binary-tree.ts | 32 +++++---- src/data-structures/binary-tree/bst.ts | 68 +++++++++++------- src/data-structures/binary-tree/rb-tree.ts | 9 ++- .../binary-tree/tree-multi-map.ts | 7 +- src/data-structures/heap/heap.ts | 2 +- src/interfaces/binary-tree.ts | 5 +- .../binary-tree/avl-tree-multi-map.ts | 5 +- .../data-structures/binary-tree/avl-tree.ts | 5 +- .../binary-tree/binary-tree.ts | 7 +- src/types/data-structures/binary-tree/bst.ts | 5 +- .../data-structures/binary-tree/rb-tree.ts | 5 +- .../binary-tree/tree-multi-map.ts | 5 +- src/types/utils/utils.ts | 15 +++- src/utils/utils.ts | 21 +++++- test/integration/index.html | 70 +++++++++++++++++++ .../binary-tree/overall.test.ts | 10 +-- .../binary-tree/tree-multi-map.test.ts | 4 +- test/unit/utils/utils.test.ts | 5 ++ 20 files changed, 226 insertions(+), 86 deletions(-) create mode 100644 test/unit/utils/utils.test.ts 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 e8b4d9b..cd449ca 100644 --- a/src/data-structures/binary-tree/avl-tree-multi-map.ts +++ b/src/data-structures/binary-tree/avl-tree-multi-map.ts @@ -12,6 +12,7 @@ import type { BinaryTreeDeleteResult, BSTNKeyOrNode, BTNCallback, + Comparable, IterationType, KeyOrNodeOrEntry } from '../../types'; @@ -19,7 +20,7 @@ import { IBinaryTree } from '../../interfaces'; import { AVLTree, AVLTreeNode } from './avl-tree'; export class AVLTreeMultiMapNode< - K = any, + K extends Comparable, V = any, NODE extends AVLTreeMultiMapNode = AVLTreeMultiMapNodeNested > extends AVLTreeNode { @@ -62,7 +63,7 @@ export class AVLTreeMultiMapNode< * The only distinction between a AVLTreeMultiMap and a AVLTree lies in the ability of the former to store duplicate nodes through the utilization of counters. */ export class AVLTreeMultiMap< - K = any, + K extends Comparable, V = any, NODE extends AVLTreeMultiMapNode = AVLTreeMultiMapNode>, TREE extends AVLTreeMultiMap = AVLTreeMultiMap> diff --git a/src/data-structures/binary-tree/avl-tree.ts b/src/data-structures/binary-tree/avl-tree.ts index 86a6cf3..db5e607 100644 --- a/src/data-structures/binary-tree/avl-tree.ts +++ b/src/data-structures/binary-tree/avl-tree.ts @@ -13,12 +13,13 @@ import type { BinaryTreeDeleteResult, BSTNKeyOrNode, BTNCallback, + Comparable, KeyOrNodeOrEntry } from '../../types'; import { IBinaryTree } from '../../interfaces'; export class AVLTreeNode< - K = any, + K extends Comparable, V = any, NODE extends AVLTreeNode = AVLTreeNodeNested > extends BSTNode { @@ -65,7 +66,7 @@ export class AVLTreeNode< * 7. Path Length: The path length from the root to any leaf is longer compared to an unbalanced BST, but shorter than a linear chain of nodes. */ export class AVLTree< - K = any, + K extends Comparable, V = any, NODE extends AVLTreeNode = AVLTreeNode>, TREE extends AVLTree = AVLTree> @@ -195,26 +196,26 @@ export class AVLTree< srcNode: BSTNKeyOrNode, destNode: BSTNKeyOrNode ): NODE | undefined { - srcNode = this.ensureNode(srcNode); - destNode = this.ensureNode(destNode); + const srcNodeEnsured = this.ensureNode(srcNode); + const destNodeEnsured = this.ensureNode(destNode); - if (srcNode && destNode) { - const { key, value, height } = destNode; + if (srcNodeEnsured && destNodeEnsured) { + const { key, value, height } = destNodeEnsured; const tempNode = this.createNode(key, value); if (tempNode) { tempNode.height = height; - destNode.key = srcNode.key; - destNode.value = srcNode.value; - destNode.height = srcNode.height; + destNodeEnsured.key = srcNodeEnsured.key; + destNodeEnsured.value = srcNodeEnsured.value; + destNodeEnsured.height = srcNodeEnsured.height; - srcNode.key = tempNode.key; - srcNode.value = tempNode.value; - srcNode.height = tempNode.height; + srcNodeEnsured.key = tempNode.key; + srcNodeEnsured.value = tempNode.value; + srcNodeEnsured.height = tempNode.height; } - return destNode; + return destNodeEnsured; } return undefined; } diff --git a/src/data-structures/binary-tree/binary-tree.ts b/src/data-structures/binary-tree/binary-tree.ts index 2f0f16b..caf9e9b 100644 --- a/src/data-structures/binary-tree/binary-tree.ts +++ b/src/data-structures/binary-tree/binary-tree.ts @@ -14,12 +14,14 @@ import type { BinaryTreePrintOptions, BTNCallback, BTNEntry, + Comparable, DFSOrderPattern, EntryCallback, + FamilyPosition, + IterationType, KeyOrNodeOrEntry, NodeDisplayLayout } from '../../types'; -import { FamilyPosition, IterationType } from '../../types'; import { IBinaryTree } from '../../interfaces'; import { trampoline } from '../../utils'; import { Queue } from '../queue'; @@ -31,7 +33,7 @@ import { IterableEntryBase } from '../base'; * @template NODE - The type of the family relationship in the binary tree. */ export class BinaryTreeNode< - K = any, + K extends Comparable, V = any, NODE extends BinaryTreeNode = BinaryTreeNode> > { @@ -129,7 +131,7 @@ export class BinaryTreeNode< */ export class BinaryTree< - K = any, + K extends Comparable, V = any, NODE extends BinaryTreeNode = BinaryTreeNode>, TREE extends BinaryTree = BinaryTree> @@ -160,7 +162,7 @@ export class BinaryTree< if (keysOrNodesOrEntries) this.addMany(keysOrNodesOrEntries); } - protected _extractor = (key: K) => (typeof key === 'number' ? key : Number(key)); + protected _extractor = (key: K | null | undefined) => (typeof key === 'number' ? key : Number(key)); /** * The function returns the value of the `_extractor` property. @@ -973,15 +975,15 @@ export class BinaryTree< * @returns the depth of the `dist` relative to the `beginRoot`. */ getDepth(dist: KeyOrNodeOrEntry, beginRoot: KeyOrNodeOrEntry = this.root): number { - dist = this.ensureNode(dist); - beginRoot = this.ensureNode(beginRoot); + let distEnsured = this.ensureNode(dist); + const beginRootEnsured = this.ensureNode(beginRoot); let depth = 0; - while (dist?.parent) { - if (dist === beginRoot) { + while (distEnsured?.parent) { + if (distEnsured === beginRootEnsured) { return depth; } depth++; - dist = dist.parent; + distEnsured = distEnsured.parent; } return depth; } @@ -1124,17 +1126,17 @@ export class BinaryTree< getPathToRoot(beginNode: KeyOrNodeOrEntry, isReverse = true): NODE[] { // TODO to support get path through passing key const result: NODE[] = []; - beginNode = this.ensureNode(beginNode); + let beginNodeEnsured = this.ensureNode(beginNode); - if (!beginNode) return result; + if (!beginNodeEnsured) return result; - while (beginNode.parent) { + while (beginNodeEnsured.parent) { // Array.push + Array.reverse is more efficient than Array.unshift // TODO may consider using Deque, so far this is not the performance bottleneck - result.push(beginNode); - beginNode = beginNode.parent; + result.push(beginNodeEnsured); + beginNodeEnsured = beginNodeEnsured.parent; } - result.push(beginNode); + result.push(beginNodeEnsured); return isReverse ? result.reverse() : result; } diff --git a/src/data-structures/binary-tree/bst.ts b/src/data-structures/binary-tree/bst.ts index e91e0da..0e4dcc5 100644 --- a/src/data-structures/binary-tree/bst.ts +++ b/src/data-structures/binary-tree/bst.ts @@ -7,22 +7,27 @@ */ import type { BSTNested, + BSTNKeyOrNode, BSTNodeNested, BSTOptions, + BSTVariant, BTNCallback, BTNodePureExemplar, + Comparable, + CP, + DFSOrderPattern, + IterationType, KeyOrNodeOrEntry } from '../../types'; -import { BSTNKeyOrNode, BSTVariant, CP, DFSOrderPattern, IterationType } from '../../types'; import { BinaryTree, BinaryTreeNode } from './binary-tree'; import { IBinaryTree } from '../../interfaces'; import { Queue } from '../queue'; -export class BSTNode = BSTNodeNested> extends BinaryTreeNode< - K, - V, - NODE -> { +export class BSTNode< + K extends Comparable, + V = any, + NODE extends BSTNode = BSTNodeNested +> extends BinaryTreeNode { override parent?: NODE; constructor(key: K, value?: V) { @@ -88,7 +93,7 @@ export class BSTNode = BSTNod * 7. No Auto-Balancing: Standard BSTs don't automatically balance themselves. */ export class BST< - K = any, + K extends Comparable, V = any, NODE extends BSTNode = BSTNode>, TREE extends BST = BST> @@ -363,8 +368,9 @@ export class BST< sorted = realBTNExemplars.sort((a, b) => { let aR: number, bR: number; - if (this.isEntry(a)) aR = this.extractor(a[0]); - else if (this.isRealNode(a)) aR = this.extractor(a.key); + if (this.isEntry(a)) { + aR = this.extractor(a[0]); + } else if (this.isRealNode(a)) aR = this.extractor(a.key); else aR = this.extractor(a); if (this.isEntry(b)) bR = this.extractor(b[0]); @@ -722,12 +728,12 @@ export class BST< targetNode: KeyOrNodeOrEntry = this.root, iterationType: IterationType = this.iterationType ): ReturnType[] { - targetNode = this.ensureNode(targetNode); + const targetNodeEnsured = this.ensureNode(targetNode); const ans: ReturnType>[] = []; - if (!targetNode) return ans; + if (!targetNodeEnsured) return ans; if (!this.root) return ans; - const targetKey = targetNode.key; + const targetKey = targetNodeEnsured.key; if (iterationType === 'RECURSIVE') { const dfs = (cur: NODE) => { @@ -898,13 +904,22 @@ export class BST< * than), 'LT' (less than), or 'EQ' (equal). */ protected _compare(a: K, b: K): CP { - const extractedA = this.extractor(a); - const extractedB = this.extractor(b); - const compared = this.variant === 'STANDARD' ? extractedA - extractedB : extractedB - extractedA; - - if (compared > 0) return 'GT'; - if (compared < 0) return 'LT'; - return 'EQ'; + if (this.variant === 'STANDARD') { + if (a > b) return 'GT'; + if (a < b) return 'LT'; + return 'EQ'; + } else { + if (a > b) return 'LT'; + if (a < b) return 'GT'; + return 'EQ'; + } + // const extractedA = this.extractor(a); + // const extractedB = this.extractor(b); + // const compared = this.variant === 'STANDARD' ? extractedA - extractedB : extractedB - extractedA; + // + // if (compared > 0) return 'GT'; + // if (compared < 0) return 'LT'; + // return 'EQ'; } /** @@ -917,9 +932,10 @@ export class BST< * @returns a boolean value. */ protected _lt(a: K, b: K): boolean { - const extractedA = this.extractor(a); - const extractedB = this.extractor(b); - return this.variant === 'STANDARD' ? extractedA < extractedB : extractedA > extractedB; + return this.variant === 'STANDARD' ? a < b : a > b; + // const extractedA = this.extractor(a); + // const extractedB = this.extractor(b); + // return this.variant === 'STANDARD' ? extractedA < extractedB : extractedA > extractedB; } /** @@ -931,8 +947,10 @@ export class BST< * @returns a boolean value. */ protected _gt(a: K, b: K): boolean { - const extractedA = this.extractor(a); - const extractedB = this.extractor(b); - return this.variant === 'STANDARD' ? extractedA > extractedB : extractedA < extractedB; + return this.variant === 'STANDARD' ? a > b : a < b; + + // const extractedA = this.extractor(a); + // const extractedB = this.extractor(b); + // return this.variant === 'STANDARD' ? extractedA > extractedB : extractedA < extractedB; } } diff --git a/src/data-structures/binary-tree/rb-tree.ts b/src/data-structures/binary-tree/rb-tree.ts index 236d035..e0e353c 100644 --- a/src/data-structures/binary-tree/rb-tree.ts +++ b/src/data-structures/binary-tree/rb-tree.ts @@ -1,17 +1,20 @@ import type { BinaryTreeDeleteResult, BTNCallback, + Comparable, + CP, + CRUD, KeyOrNodeOrEntry, + RBTNColor, RBTreeOptions, RedBlackTreeNested, RedBlackTreeNodeNested } from '../../types'; -import { CP, CRUD, RBTNColor } from '../../types'; import { BST, BSTNode } from './bst'; import { IBinaryTree } from '../../interfaces'; export class RedBlackTreeNode< - K = any, + K extends Comparable, V = any, NODE extends RedBlackTreeNode = RedBlackTreeNodeNested > extends BSTNode { @@ -51,7 +54,7 @@ export class RedBlackTreeNode< } export class RedBlackTree< - K = any, + K extends Comparable, V = any, NODE extends RedBlackTreeNode = RedBlackTreeNode>, TREE extends RedBlackTree = RedBlackTree> diff --git a/src/data-structures/binary-tree/tree-multi-map.ts b/src/data-structures/binary-tree/tree-multi-map.ts index 7ad13d9..1cad83c 100644 --- a/src/data-structures/binary-tree/tree-multi-map.ts +++ b/src/data-structures/binary-tree/tree-multi-map.ts @@ -9,18 +9,19 @@ import type { BinaryTreeDeleteResult, BSTNKeyOrNode, BTNCallback, + Comparable, IterationType, KeyOrNodeOrEntry, + RBTNColor, TreeMultiMapNested, TreeMultiMapNodeNested, TreeMultiMapOptions } from '../../types'; -import { RBTNColor } from '../../types'; import { IBinaryTree } from '../../interfaces'; import { RedBlackTree, RedBlackTreeNode } from './rb-tree'; export class TreeMultiMapNode< - K = any, + K extends Comparable, V = any, NODE extends TreeMultiMapNode = TreeMultiMapNodeNested > extends RedBlackTreeNode { @@ -62,7 +63,7 @@ export class TreeMultiMapNode< } export class TreeMultiMap< - K = any, + K extends Comparable, V = any, NODE extends TreeMultiMapNode = TreeMultiMapNode>, TREE extends TreeMultiMap = TreeMultiMap> diff --git a/src/data-structures/heap/heap.ts b/src/data-structures/heap/heap.ts index 9077687..665b3a0 100644 --- a/src/data-structures/heap/heap.ts +++ b/src/data-structures/heap/heap.ts @@ -93,7 +93,7 @@ export class Heap extends IterableElementBase { * @param elements * @param options */ - static heapify(elements: Iterable, options: { comparator: Comparator }): Heap { + static heapify(elements: Iterable, options: HeapOptions): Heap { return new Heap(elements, options); } diff --git a/src/interfaces/binary-tree.ts b/src/interfaces/binary-tree.ts index d6c9caf..5dba15f 100644 --- a/src/interfaces/binary-tree.ts +++ b/src/interfaces/binary-tree.ts @@ -1,15 +1,16 @@ import { BinaryTree, BinaryTreeNode } from '../data-structures'; -import { +import type { BinaryTreeDeleteResult, BinaryTreeNested, BinaryTreeNodeNested, BinaryTreeOptions, BTNCallback, + Comparable, KeyOrNodeOrEntry } from '../types'; export interface IBinaryTree< - K = number, + K extends Comparable, V = any, N extends BinaryTreeNode = BinaryTreeNodeNested, TREE extends BinaryTree = BinaryTreeNested 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 ac54c68..a8d4136 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,9 @@ import { AVLTreeMultiMap, AVLTreeMultiMapNode } from '../../../data-structures'; import type { AVLTreeOptions } from './avl-tree'; +import { Comparable } from "../../utils"; -export type AVLTreeMultiMapNodeNested = AVLTreeMultiMapNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type AVLTreeMultiMapNodeNested = AVLTreeMultiMapNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -export type AVLTreeMultiMapNested> = AVLTreeMultiMap>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type AVLTreeMultiMapNested> = AVLTreeMultiMap>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 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 eb43fe0..b69d454 100644 --- a/src/types/data-structures/binary-tree/avl-tree.ts +++ b/src/types/data-structures/binary-tree/avl-tree.ts @@ -1,8 +1,9 @@ import { AVLTree, AVLTreeNode } from '../../../data-structures'; import { BSTOptions } from './bst'; +import { Comparable } from "../../utils"; -export type AVLTreeNodeNested = AVLTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type AVLTreeNodeNested = AVLTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -export type AVLTreeNested> = AVLTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type AVLTreeNested> = AVLTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 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 1fb398a..e3d2c0e 100644 --- a/src/types/data-structures/binary-tree/binary-tree.ts +++ b/src/types/data-structures/binary-tree/binary-tree.ts @@ -1,11 +1,12 @@ import { BinaryTree, BinaryTreeNode } from '../../../data-structures'; import { IterationType } from "../../common"; +import { Comparable } from "../../utils"; -export type BinaryTreeNodeNested = BinaryTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type BinaryTreeNodeNested = BinaryTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -export type BinaryTreeNested> = BinaryTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type BinaryTreeNested> = BinaryTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> export type BinaryTreeOptions = { iterationType?: IterationType, - extractor?: (key: K) => number + extractor?: (key: K | null | undefined) => number } diff --git a/src/types/data-structures/binary-tree/bst.ts b/src/types/data-structures/binary-tree/bst.ts index 131f9a2..d4853fa 100644 --- a/src/types/data-structures/binary-tree/bst.ts +++ b/src/types/data-structures/binary-tree/bst.ts @@ -1,10 +1,11 @@ import { BST, BSTNode } from '../../../data-structures'; import type { BinaryTreeOptions } from './binary-tree'; import { BSTVariant } from "../../common"; +import { Comparable } from "../../utils"; -export type BSTNodeNested = BSTNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type BSTNodeNested = BSTNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -export type BSTNested> = BST>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type BSTNested> = BST>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> export type BSTOptions = BinaryTreeOptions & { variant?: BSTVariant diff --git a/src/types/data-structures/binary-tree/rb-tree.ts b/src/types/data-structures/binary-tree/rb-tree.ts index 44891aa..d44512b 100644 --- a/src/types/data-structures/binary-tree/rb-tree.ts +++ b/src/types/data-structures/binary-tree/rb-tree.ts @@ -1,10 +1,11 @@ import { RedBlackTree, RedBlackTreeNode } from '../../../data-structures'; import type { BSTOptions } from "./bst"; +import { Comparable } from "../../utils"; export type RBTNColor = 'RED' | 'BLACK'; -export type RedBlackTreeNodeNested = RedBlackTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type RedBlackTreeNodeNested = RedBlackTreeNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -export type RedBlackTreeNested> = RedBlackTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type RedBlackTreeNested> = RedBlackTree>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> export type RBTreeOptions = Omit, 'variant'> & {}; 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 3277da1..426744b 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,9 @@ import { TreeMultiMap, TreeMultiMapNode } from '../../../data-structures'; import type { RBTreeOptions } from './rb-tree'; +import { Comparable } from "../../utils"; -export type TreeMultiMapNodeNested = TreeMultiMapNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type TreeMultiMapNodeNested = TreeMultiMapNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -export type TreeMultiMapNested> = TreeMultiMap>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type TreeMultiMapNested> = TreeMultiMap>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> export type TreeMultiMapOptions = RBTreeOptions & {} diff --git a/src/types/utils/utils.ts b/src/types/utils/utils.ts index 46923fd..fd55689 100644 --- a/src/types/utils/utils.ts +++ b/src/types/utils/utils.ts @@ -5,4 +5,17 @@ export type TrlAsyncFn = (...args: any[]) => any; export type SpecifyOptional = Omit & Partial>; -export type Any = string | number | boolean | object | null | undefined | symbol; +export type Any = string | number | bigint | boolean | symbol | undefined | object; + +export type Comparable = + | number + | string + | bigint + | boolean + | ({ [key in string]: any } & { + valueOf(): Comparable; +}) + | ({ [key in string]: any } & { + toString(): Comparable; +}) + | (() => Comparable); diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 0ca8482..7a7d850 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -5,7 +5,7 @@ * @copyright Copyright (c) 2022 Tyler Zeng * @license MIT License */ -import type { Thunk, ToThunkFn, TrlAsyncFn, TrlFn } from '../types'; +import type { Comparable, Thunk, ToThunkFn, TrlAsyncFn, TrlFn } from '../types'; export const uuidV4 = function () { return 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.replace(/[x]/g, function (c) { @@ -105,3 +105,22 @@ export const roundFixed = (num: number, digit: number = 10) => { const multiplier = Math.pow(10, digit); return Math.round(num * multiplier) / multiplier; }; + +export function isComparable(key: any): key is Comparable { + const keyType = typeof key; + if (keyType === 'number') return isNaN(key); + if (keyType === 'string') return true; + if (keyType === 'bigint') return true; + if (keyType === 'boolean') return true; + if (keyType === 'symbol') return false; + if (keyType === 'undefined') return false; + if (keyType === 'function') return isComparable(key()); + if (keyType === 'object') { + if (key === null) return true; + if (typeof key.valueOf === 'function') return isComparable(key.valueOf()); + if (typeof key.toString === 'function') return isComparable(key.toString()); + return false; + } + + return false; +} diff --git a/test/integration/index.html b/test/integration/index.html index 41866b4..d44a80b 100644 --- a/test/integration/index.html +++ b/test/integration/index.html @@ -371,6 +371,76 @@ } catch (e) { console.error(e); } + + function heapTest() { + const heap = new dataStructureTyped.Heap([], { comparator: (a, b) => b - a }); + + heap.add(10); + heap.add(5); + heap.add(15); + + const max = heap.poll(); + console.log(max); // 15 + + const max2 = heap.peek(); + console.log(max2); // 15 + + let isEmpty = heap.isEmpty(); + console.log(isEmpty); // false + + heap.clear(); + isEmpty = heap.isEmpty(); + console.log(isEmpty); // true + + const minNumHeap = new dataStructureTyped.MinHeap([1, 6, 2, 0, 5]); + minNumHeap.add(9); + minNumHeap.has(1) // true + minNumHeap.has(2) // true + minNumHeap.poll() // 0 + minNumHeap.poll() // 1 + minNumHeap.peek() // 2 + minNumHeap.has(1); // false + minNumHeap.has(2); // true + + const arrFromHeap = minNumHeap.toArray(); + arrFromHeap.length // 4 + arrFromHeap[0] // 2 + arrFromHeap[1] // 5 + arrFromHeap[2] // 9 + arrFromHeap[3] // 6 + minNumHeap.sort() // [2, 5, 6, 9] + + const maxHeap = new dataStructureTyped.MaxHeap([], { comparator: (a, b) => b.keyA - a.keyA }); + const obj1 = { keyA: 'a1' }, obj6 = { keyA: 'a6' }, obj5 = { keyA: 'a5' }, obj2 = { keyA: 'a2' }, + obj0 = { keyA: 'a0' }, obj9 = { keyA: 'a9' }; + + maxHeap.add(obj1); + maxHeap.has(obj1) // true + maxHeap.has(obj9) // false + maxHeap.add(obj6); + maxHeap.has(obj6) // true + maxHeap.add(obj5); + maxHeap.add(obj2); + maxHeap.add(obj0); + maxHeap.add(obj9); + maxHeap.has(obj9) // true + + const peek9 = maxHeap.peek(); + console.log(peek9.keyA) // 'a9' + + const heapToArr = maxHeap.toArray(); + console.log(heapToArr.map(ele => ele?.keyA)); // ['a9', 'a2', 'a6', 'a1', 'a0', 'a5'] + + const values = ['a9', 'a6', 'a5', 'a2', 'a1', 'a0']; + let i = 0; + while (maxHeap.size > 0) { + const polled = maxHeap.poll(); + console.log(polled.keyA) // values[i] + i++; + } + } + + diff --git a/test/unit/data-structures/binary-tree/overall.test.ts b/test/unit/data-structures/binary-tree/overall.test.ts index f83394c..33db622 100644 --- a/test/unit/data-structures/binary-tree/overall.test.ts +++ b/test/unit/data-structures/binary-tree/overall.test.ts @@ -2,7 +2,7 @@ import { AVLTree, BST, RedBlackTree, TreeMultiMap } from '../../../../src'; describe('Overall BinaryTree Test', () => { it('should perform various operations on BinaryTree', () => { - const bst = new BST(); + const bst = new BST(); bst.add(11); bst.add(3); bst.addMany([15, 1, 8, 13, 16, 2, 6, 9, 12, 14, 4, 7, 10, 5], undefined, false); @@ -65,7 +65,7 @@ describe('Overall BinaryTree Test', () => { const bst = new BST([3, 6, 7, 1, 9], { iterationType: 'RECURSIVE', variant: 'INVERSE', - extractor: key => key + extractor: key => Number(key) }); expect(bst.size).toBe(5); expect(bst.root?.key).toBe(6); @@ -106,7 +106,7 @@ describe('Overall BinaryTree Test', () => { const avl = new AVLTree([3, 6, 7, 1, 9], { iterationType: 'RECURSIVE', variant: 'INVERSE', - extractor: key => key + extractor: key => Number(key) }); expect(avl.size).toBe(5); avl.add(2); @@ -149,7 +149,7 @@ describe('Overall BinaryTree Test', () => { it('Should clone a TreeMultiMap works fine', () => { const tmm = new TreeMultiMap([3, 6, 7, 1, 9], { iterationType: 'RECURSIVE', - extractor: key => key + extractor: key => Number(key) }); expect(tmm.size).toBe(5); tmm.add(2); @@ -198,7 +198,7 @@ describe('Overall BinaryTree Test', () => { it('Should clone a RedBlackTree works fine', () => { const rbTree = new RedBlackTree([3, 6, 7, 1, 9], { iterationType: 'RECURSIVE', - extractor: key => key + extractor: key => Number(key) }); expect(rbTree.size).toBe(5); rbTree.add(2); 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 a3bf093..3a0751b 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 @@ -93,7 +93,7 @@ describe('TreeMultiMap operations test1', () => { }); it('should perform various operations on a Binary Search Tree with numeric values1', () => { - const tmm = new TreeMultiMap(); + const tmm = new TreeMultiMap(); expect(tmm instanceof TreeMultiMap); @@ -152,7 +152,7 @@ describe('TreeMultiMap operations test1', () => { node15 && tmm.dfs(node => (subTreeSum += node.key), 'PRE', 15); expect(subTreeSum).toBe(45); let lesserSum = 0; - tmm.lesserOrGreaterTraverse((node: TreeMultiMapNode) => (lesserSum += node.key), 'LT', 10); + tmm.lesserOrGreaterTraverse(node => (lesserSum += node.key), 'LT', 10); expect(lesserSum).toBe(45); expect(node15 instanceof TreeMultiMapNode); diff --git a/test/unit/utils/utils.test.ts b/test/unit/utils/utils.test.ts new file mode 100644 index 0000000..4f1e137 --- /dev/null +++ b/test/unit/utils/utils.test.ts @@ -0,0 +1,5 @@ +describe('isNaN', () => { + it('should isNaN', function () { + expect(isNaN('string' as unknown as number)).toBe(true); + }); +});