From 07dd75de705a1fc4e22d1ec531ae2849d4bb6afd Mon Sep 17 00:00:00 2001 From: Revone Date: Wed, 16 Oct 2024 21:55:48 +1300 Subject: [PATCH] test: The test cases have been added, though slightly tricky. The overall test coverage is currently at 93.27%. --- src/data-structures/hash/hash-map.ts | 82 ++--- .../linked-list/singly-linked-list.ts | 2 +- src/data-structures/queue/deque.ts | 3 +- src/data-structures/queue/queue.ts | 2 +- src/data-structures/tree/tree.ts | 1 - .../binary-tree/avl-tree-multi-map.test.ts | 18 +- .../binary-tree/avl-tree.test.ts | 18 +- .../binary-tree/binary-tree.test.ts | 20 +- .../data-structures/binary-tree/bst.test.ts | 22 +- .../binary-tree/rb-tree.test.ts | 12 +- .../binary-tree/tree-multi-map.test.ts | 18 +- .../graph/directed-graph.test.ts | 78 ++-- .../data-structures/graph/map-graph.test.ts | 26 +- .../graph/undirected-graph.test.ts | 48 +-- .../data-structures/hash/hash-map.test.ts | 189 ++++++++-- test/unit/data-structures/heap/heap.test.ts | 86 +++-- .../data-structures/heap/min-heap.test.ts | 10 +- .../linked-list/doubly-linked-list.test.ts | 37 +- .../linked-list/singly-linked-list.test.ts | 32 ++ .../data-structures/matrix/matrix.test.ts | 40 ++- .../priority-queue/max-priority-queue.test.ts | 31 ++ .../priority-queue/min-priority-queue.test.ts | 17 + .../priority-queue/priority-queue.test.ts | 17 + test/unit/data-structures/queue/deque.test.ts | 335 +++++++++++++++--- test/unit/data-structures/queue/queue.test.ts | 99 ++++-- test/unit/data-structures/stack/stack.test.ts | 24 +- test/unit/data-structures/tree/tree.test.ts | 58 +++ test/unit/data-structures/trie/trie.test.ts | 51 ++- 28 files changed, 1024 insertions(+), 352 deletions(-) create mode 100644 test/unit/data-structures/tree/tree.test.ts diff --git a/src/data-structures/hash/hash-map.ts b/src/data-structures/hash/hash-map.ts index 42a9235..5f8534a 100644 --- a/src/data-structures/hash/hash-map.ts +++ b/src/data-structures/hash/hash-map.ts @@ -163,8 +163,8 @@ export class HashMap extends IterableEntryBase extends IterableEntryBase { - return new HashMap(this, { hashFn: this.hashFn, toEntryFn: this.toEntryFn }); + return new HashMap(this, { hashFn: this._hashFn, toEntryFn: this._toEntryFn }); } /** @@ -302,18 +302,6 @@ export class HashMap extends IterableEntryBase extends IterableEntryBase extends IterableEntryBa * @param [options] - The `options` parameter is an optional object that can contain the following * properties: */ - constructor(entryOrRawElements: Iterable = [], options?: LinkedHashMapOptions) { + constructor(entryOrRawElements: Iterable = [], options?: LinkedHashMapOptions) { super(); this._sentinel = >{}; this._sentinel.prev = this._sentinel.next = this._head = this._tail = this._sentinel; @@ -395,10 +383,7 @@ export class LinkedHashMap extends IterableEntryBa } if (entryOrRawElements) { - for (const el of entryOrRawElements) { - const [key, value] = this.toEntryFn(el); - this.set(key, value); - } + this.setMany(entryOrRawElements); } } @@ -465,7 +450,7 @@ export class LinkedHashMap extends IterableEntryBa return this._tail; } - protected _toEntryFn: (rawElement: R) => [K, V] = (rawElement: R) => { + protected _toEntryFn?: (rawElement: R) => [K, V] = (rawElement: R) => { if (this.isEntry(rawElement)) { // TODO, For performance optimization, it may be necessary to only inspect the first element traversed. return rawElement; @@ -575,7 +560,7 @@ export class LinkedHashMap extends IterableEntryBa const isNewKey = !this.has(key); // Check if the key is new if (isWeakKey(key)) { - const hash = this.objHashFn(key); + const hash = this._objHashFn(key); node = this.objMap.get(hash); if (!node && isNewKey) { @@ -587,7 +572,7 @@ export class LinkedHashMap extends IterableEntryBa node.value = value; } } else { - const hash = this.hashFn(key); + const hash = this._hashFn(key); node = this.noObjMap[hash]; if (!node && isNewKey) { @@ -623,11 +608,20 @@ export class LinkedHashMap extends IterableEntryBa * R. * @returns The `setMany` function returns an array of booleans. */ - setMany(entryOrRawElements: Iterable): boolean[] { + setMany(entryOrRawElements: Iterable): boolean[] { const results: boolean[] = []; for (const rawEle of entryOrRawElements) { - const [key, value] = this.toEntryFn(rawEle); - results.push(this.set(key, value)); + let key: K | undefined, value: V | undefined; + if (this.isEntry(rawEle)) { + key = rawEle[0]; + value = rawEle[1]; + } else if (this._toEntryFn) { + const item = this._toEntryFn(rawEle); + key = item[0]; + value = item[1]; + } + + if (key !== undefined && value !== undefined) results.push(this.set(key, value)); } return results; } @@ -640,10 +634,10 @@ export class LinkedHashMap extends IterableEntryBa */ override has(key: K): boolean { if (isWeakKey(key)) { - const hash = this.objHashFn(key); + const hash = this._objHashFn(key); return this.objMap.has(hash); } else { - const hash = this.hashFn(key); + const hash = this._hashFn(key); return hash in this.noObjMap; } } @@ -668,11 +662,11 @@ export class LinkedHashMap extends IterableEntryBa */ override get(key: K): V | undefined { if (isWeakKey(key)) { - const hash = this.objHashFn(key); + const hash = this._objHashFn(key); const node = this.objMap.get(hash); return node ? node.value : undefined; } else { - const hash = this.hashFn(key); + const hash = this._hashFn(key); const node = this.noObjMap[hash]; return node ? node.value : undefined; } @@ -722,7 +716,7 @@ export class LinkedHashMap extends IterableEntryBa let node; if (isWeakKey(key)) { - const hash = this.objHashFn(key); + const hash = this._objHashFn(key); // Get nodes from WeakMap node = this.objMap.get(hash); @@ -733,7 +727,7 @@ export class LinkedHashMap extends IterableEntryBa // Remove nodes from WeakMap this.objMap.delete(hash); } else { - const hash = this.hashFn(key); + const hash = this._hashFn(key); // Get nodes from noObjMap node = this.noObjMap[hash]; @@ -832,7 +826,7 @@ export class LinkedHashMap extends IterableEntryBa * of the original `LinkedHashMap` object. */ clone(): LinkedHashMap { - const cloned = new LinkedHashMap([], { hashFn: this.hashFn, objHashFn: this.objHashFn }); + const cloned = new LinkedHashMap([], { hashFn: this._hashFn, objHashFn: this._objHashFn }); for (const entry of this) { const [key, value] = entry; cloned.set(key, value); @@ -905,26 +899,6 @@ export class LinkedHashMap extends IterableEntryBa return mappedMap; } - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - */ - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The put function sets a value in a data structure using a specified key. - * @param {K} key - The key parameter is of type K, which represents the type of the key being passed - * to the function. - * @param {V} value - The value parameter represents the value that you want to associate with the - * specified key in the data structure. - * @returns The method is returning a boolean value. - */ - put(key: K, value: V): boolean { - return this.set(key, value); - } - /** * Time Complexity: O(n) * Space Complexity: O(1) diff --git a/src/data-structures/linked-list/singly-linked-list.ts b/src/data-structures/linked-list/singly-linked-list.ts index b2026ba..21fdc23 100644 --- a/src/data-structures/linked-list/singly-linked-list.ts +++ b/src/data-structures/linked-list/singly-linked-list.ts @@ -350,7 +350,7 @@ export class SinglyLinkedList extends IterableElementBase | undefined): boolean { - if (!valueOrNode) return false; + if (valueOrNode === undefined) return false; let value: E; if (valueOrNode instanceof SinglyLinkedListNode) { value = valueOrNode.value; diff --git a/src/data-structures/queue/deque.ts b/src/data-structures/queue/deque.ts index 4ce48f5..617b37a 100644 --- a/src/data-structures/queue/deque.ts +++ b/src/data-structures/queue/deque.ts @@ -507,7 +507,6 @@ export class Deque extends IterableElementBase { if (isCutSelf) { if (pos < 0) { - this.clear(); return this; } const { bucketIndex, indexInBucket } = this._getBucketAndPosition(pos); @@ -517,7 +516,7 @@ export class Deque extends IterableElementBase([], { bucketSize: this._bucketSize }); - + if (pos < 0) pos = 0; for (let i = pos; i < this.size; i++) { newDeque.push(this.at(i)); } diff --git a/src/data-structures/queue/queue.ts b/src/data-structures/queue/queue.ts index db71e13..6dcdcf7 100644 --- a/src/data-structures/queue/queue.ts +++ b/src/data-structures/queue/queue.ts @@ -95,7 +95,7 @@ export class Queue extends IterableElementBase 0 ? this.elements[this.elements.length - 1] : undefined; } - _autoCompactRatio: number = 0.5; + protected _autoCompactRatio: number = 0.5; /** * This function returns the value of the autoCompactRatio property. diff --git a/src/data-structures/tree/tree.ts b/src/data-structures/tree/tree.ts index c0e85e5..eeb3d50 100644 --- a/src/data-structures/tree/tree.ts +++ b/src/data-structures/tree/tree.ts @@ -12,7 +12,6 @@ export class TreeNode { constructor(key: string, value?: V, children?: TreeNode[]) { this._key = key; this._value = value || undefined; - this._children = children || []; } protected _key: string; 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 0f3a865..6dc1e38 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 @@ -617,14 +617,14 @@ describe('AVLTreeMultiMap iterative methods test', () => { treeMM.add([3, 'c'], undefined, 1); }); - test('The node obtained by get Node should match the node type', () => { + it('The node obtained by get Node should match the node type', () => { const node3 = treeMM.getNode(3); expect(node3).toBeInstanceOf(BinaryTreeNode); expect(node3).toBeInstanceOf(BSTNode); expect(node3).toBeInstanceOf(AVLTreeNode); }); - test('forEach should iterate over all elements', () => { + it('forEach should iterate over all elements', () => { const mockCallback = jest.fn(); treeMM.forEach((value, key) => { mockCallback(value, key); @@ -636,7 +636,7 @@ describe('AVLTreeMultiMap iterative methods test', () => { expect(mockCallback.mock.calls[2]).toEqual(['c', 3]); }); - test('filter should return a new tree with filtered elements', () => { + it('filter should return a new tree with filtered elements', () => { const filteredTree = treeMM.filter((value, key) => key > 1); expect(filteredTree.size).toBe(2); expect([...filteredTree]).toEqual([ @@ -645,7 +645,7 @@ describe('AVLTreeMultiMap iterative methods test', () => { ]); }); - test('map should return a new tree with modified elements', () => { + it('map should return a new tree with modified elements', () => { const mappedTree = treeMM.map((value, key) => (key * 2).toString()); expect(mappedTree.size).toBe(3); expect([...mappedTree]).toEqual([ @@ -655,12 +655,12 @@ describe('AVLTreeMultiMap iterative methods test', () => { ]); }); - test('reduce should accumulate values', () => { + it('reduce should accumulate values', () => { const sum = treeMM.reduce((acc, value, key) => acc + key, 0); expect(sum).toBe(6); }); - test('[Symbol.iterator] should provide an iterator', () => { + it('[Symbol.iterator] should provide an iterator', () => { const entries = []; for (const entry of treeMM) { entries.push(entry); @@ -674,19 +674,19 @@ describe('AVLTreeMultiMap iterative methods test', () => { ]); }); - test('should clone work well', () => { + it('should clone work well', () => { expect(treeMM.count).toBe(21); const cloned = treeMM.clone(); expect(cloned.root?.left?.key).toBe(1); expect(cloned.root?.right?.value).toBe('c'); }); - test('should keys', () => { + it('should keys', () => { const keys = treeMM.keys(); expect([...keys]).toEqual([1, 2, 3]); }); - test('should values', () => { + it('should values', () => { const values = treeMM.values(); expect([...values]).toEqual(['a', 'b', 'c']); }); 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 d5d74b5..c972585 100644 --- a/test/unit/data-structures/binary-tree/avl-tree.test.ts +++ b/test/unit/data-structures/binary-tree/avl-tree.test.ts @@ -339,14 +339,14 @@ describe('AVLTree iterative methods test', () => { avl.add([3, 'c']); }); - test('The node obtained by get Node should match the node type', () => { + it('The node obtained by get Node should match the node type', () => { const node3 = avl.getNode(3); expect(node3).toBeInstanceOf(BinaryTreeNode); expect(node3).toBeInstanceOf(BSTNode); expect(node3).toBeInstanceOf(AVLTreeNode); }); - test('forEach should iterate over all elements', () => { + it('forEach should iterate over all elements', () => { const mockCallback = jest.fn(); avl.forEach((value, key) => { mockCallback(value, key); @@ -358,7 +358,7 @@ describe('AVLTree iterative methods test', () => { expect(mockCallback.mock.calls[2]).toEqual(['c', 3]); }); - test('filter should return a new tree with filtered elements', () => { + it('filter should return a new tree with filtered elements', () => { const filteredTree = avl.filter((value, key) => key > 1); expect(filteredTree.size).toBe(2); expect([...filteredTree]).toEqual([ @@ -367,7 +367,7 @@ describe('AVLTree iterative methods test', () => { ]); }); - test('map should return a new tree with modified elements', () => { + it('map should return a new tree with modified elements', () => { const mappedTree = avl.map((value, key) => (key * 2).toString()); expect(mappedTree.size).toBe(3); expect([...mappedTree]).toEqual([ @@ -377,12 +377,12 @@ describe('AVLTree iterative methods test', () => { ]); }); - test('reduce should accumulate values', () => { + it('reduce should accumulate values', () => { const sum = avl.reduce((acc, value, key) => acc + key, 0); expect(sum).toBe(6); }); - test('[Symbol.iterator] should provide an iterator', () => { + it('[Symbol.iterator] should provide an iterator', () => { const entries = []; for (const entry of avl) { entries.push(entry); @@ -396,18 +396,18 @@ describe('AVLTree iterative methods test', () => { ]); }); - test('should clone work well', () => { + it('should clone work well', () => { const cloned = avl.clone(); expect(cloned.root?.left?.key).toBe(1); expect(cloned.root?.right?.value).toBe('c'); }); - test('should keys', () => { + it('should keys', () => { const keys = avl.keys(); expect([...keys]).toEqual([1, 2, 3]); }); - test('should values', () => { + it('should values', () => { const values = avl.values(); expect([...values]).toEqual(['a', 'b', '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 be2fea1..901ad69 100644 --- a/test/unit/data-structures/binary-tree/binary-tree.test.ts +++ b/test/unit/data-structures/binary-tree/binary-tree.test.ts @@ -767,12 +767,12 @@ describe('BinaryTree iterative methods test', () => { binaryTree.add([3, 'c']); }); - test('The node obtained by get Node should match the node type', () => { + it('The node obtained by get Node should match the node type', () => { const node3 = binaryTree.getNode(3); expect(node3).toBeInstanceOf(BinaryTreeNode); }); - test('forEach should iterate over all elements', () => { + it('forEach should iterate over all elements', () => { const mockCallback = jest.fn(); binaryTree.forEach((value, key) => { mockCallback(value, key); @@ -784,7 +784,7 @@ describe('BinaryTree iterative methods test', () => { expect(mockCallback.mock.calls[2]).toEqual(['c', 3]); }); - test('filter should return a new tree with filtered elements', () => { + it('filter should return a new tree with filtered elements', () => { const filteredTree = binaryTree.filter((value, key) => key > 1); expect(filteredTree.size).toBe(2); expect([...filteredTree]).toEqual([ @@ -793,7 +793,7 @@ describe('BinaryTree iterative methods test', () => { ]); }); - test('map should return a new tree with modified elements', () => { + it('map should return a new tree with modified elements', () => { const mappedTree = binaryTree.map((value, key) => (key * 2).toString()); expect(mappedTree.size).toBe(3); expect([...mappedTree]).toEqual([ @@ -803,12 +803,12 @@ describe('BinaryTree iterative methods test', () => { ]); }); - test('reduce should accumulate values', () => { + it('reduce should accumulate values', () => { const sum = binaryTree.reduce((acc, currentValue, currentKey) => acc + currentKey, 0); expect(sum).toBe(6); }); - test('[Symbol.iterator] should provide an iterator', () => { + it('[Symbol.iterator] should provide an iterator', () => { const entries = []; for (const entry of binaryTree) { entries.push(entry); @@ -822,23 +822,23 @@ describe('BinaryTree iterative methods test', () => { ]); }); - test('should clone work well', () => { + it('should clone work well', () => { const cloned = binaryTree.clone(); expect(cloned.root?.left?.key).toBe(2); expect(cloned.root?.right?.value).toBe('c'); }); - test('should keys', () => { + it('should keys', () => { const keys = binaryTree.keys(); expect([...keys]).toEqual([2, 1, 3]); }); - test('should values', () => { + it('should values', () => { const values = binaryTree.values(); expect([...values]).toEqual(['b', 'a', 'c']); }); - test('should iterative method return undefined when the node is null', () => { + it('should iterative method return undefined when the node is null', () => { const tree = new BinaryTree(); tree.addMany([-10, -10, -10, 9, 9, 20, null, null, 15, 7, 8, null, 2, null, 6, null, null, 8, 8, 8]); const bfsResult = tree.bfs(undefined, undefined, undefined, true); diff --git a/test/unit/data-structures/binary-tree/bst.test.ts b/test/unit/data-structures/binary-tree/bst.test.ts index 1a7a404..4be0786 100644 --- a/test/unit/data-structures/binary-tree/bst.test.ts +++ b/test/unit/data-structures/binary-tree/bst.test.ts @@ -873,13 +873,13 @@ describe('BST operations test recursively', () => { }); describe('BST isBST', function () { - test('isBST', () => { + it('isBST', () => { const bst = new BST(); bst.addMany([1, 2, 3, 9, 8, 5, 6, 7, 4]); expect(bst.isBST()).toBe(true); }); - test('isBST when variant is Max', () => { + it('isBST when variant is Max', () => { const bst = new BST([1, 2, 3, 9, 8, 5, 6, 7, 4], { comparator: (a, b) => b - a }); @@ -961,13 +961,13 @@ describe('BST iterative methods test', () => { bst.add([3, 'c']); }); - test('The node obtained by get Node should match the node type', () => { + it('The node obtained by get Node should match the node type', () => { const node3 = bst.getNode(3); expect(node3).toBeInstanceOf(BinaryTreeNode); expect(node3).toBeInstanceOf(BSTNode); }); - test('forEach should iterate over all elements', () => { + it('forEach should iterate over all elements', () => { const mockCallback = jest.fn(); bst.forEach((value, key) => { mockCallback(value, key); @@ -979,7 +979,7 @@ describe('BST iterative methods test', () => { expect(mockCallback.mock.calls[2]).toEqual(['c', 3]); }); - test('filter should return a new tree with filtered elements', () => { + it('filter should return a new tree with filtered elements', () => { const filteredTree = bst.filter((value, key) => key > 1); expect(filteredTree.size).toBe(2); expect([...filteredTree]).toEqual([ @@ -988,7 +988,7 @@ describe('BST iterative methods test', () => { ]); }); - test('map should return a new tree with modified elements', () => { + it('map should return a new tree with modified elements', () => { const mappedTree = bst.map((value, key) => (key * 2).toString()); expect(mappedTree.size).toBe(3); expect([...mappedTree]).toEqual([ @@ -998,12 +998,12 @@ describe('BST iterative methods test', () => { ]); }); - test('reduce should accumulate values', () => { + it('reduce should accumulate values', () => { const sum = bst.reduce((acc, value, key) => acc + key, 0); expect(sum).toBe(6); }); - test('[Symbol.iterator] should provide an iterator', () => { + it('[Symbol.iterator] should provide an iterator', () => { const entries = []; for (const entry of bst) { entries.push(entry); @@ -1017,18 +1017,18 @@ describe('BST iterative methods test', () => { ]); }); - test('should clone work well', () => { + it('should clone work well', () => { const cloned = bst.clone(); expect(cloned.root?.left).toBe(undefined); expect(cloned.root?.right?.value).toBe('b'); }); - test('should keys', () => { + it('should keys', () => { const keys = bst.keys(); expect([...keys]).toEqual([1, 2, 3]); }); - test('should values', () => { + it('should values', () => { const values = bst.values(); expect([...values]).toEqual(['a', 'b', 'c']); }); diff --git a/test/unit/data-structures/binary-tree/rb-tree.test.ts b/test/unit/data-structures/binary-tree/rb-tree.test.ts index a5a8760..3019fdc 100644 --- a/test/unit/data-structures/binary-tree/rb-tree.test.ts +++ b/test/unit/data-structures/binary-tree/rb-tree.test.ts @@ -619,14 +619,14 @@ describe('RedBlackTree 2', () => { rbTree.add([3, 'c']); }); - test('The node obtained by get Node should match the node type', () => { + it('The node obtained by get Node should match the node type', () => { const node3 = rbTree.getNode(3); expect(node3).toBeInstanceOf(BinaryTreeNode); expect(node3).toBeInstanceOf(BSTNode); expect(node3).toBeInstanceOf(RedBlackTreeNode); }); - test('forEach should iterate over all elements', () => { + it('forEach should iterate over all elements', () => { const mockCallback = jest.fn(); rbTree.forEach((value, key) => { mockCallback(value, key); @@ -638,7 +638,7 @@ describe('RedBlackTree 2', () => { expect(mockCallback.mock.calls[2]).toEqual(['c', 3]); }); - test('filter should return a new rbTree with filtered elements', () => { + it('filter should return a new rbTree with filtered elements', () => { const filteredTree = rbTree.filter((value, key) => key > 1); expect(filteredTree.size).toBe(2); expect([...filteredTree]).toEqual([ @@ -647,7 +647,7 @@ describe('RedBlackTree 2', () => { ]); }); - test('map should return a new rbTree with modified elements', () => { + it('map should return a new rbTree with modified elements', () => { const mappedTree = rbTree.map((value, key) => (key * 2).toString()); expect(mappedTree.size).toBe(3); expect([...mappedTree]).toEqual([ @@ -657,12 +657,12 @@ describe('RedBlackTree 2', () => { ]); }); - test('reduce should accumulate values', () => { + it('reduce should accumulate values', () => { const sum = rbTree.reduce((acc, value, key) => acc + key, 0); expect(sum).toBe(6); }); - test('[Symbol.iterator] should provide an iterator', () => { + it('[Symbol.iterator] should provide an iterator', () => { const entries = []; for (const entry of rbTree) { entries.push(entry); 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 849be64..215c4a7 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 @@ -755,14 +755,14 @@ describe('TreeMultiMap iterative methods test', () => { treeMM.add([3, 'c'], undefined, 1); }); - test('The node obtained by get Node should match the node type', () => { + it('The node obtained by get Node should match the node type', () => { const node3 = treeMM.getNode(3); expect(node3).toBeInstanceOf(BinaryTreeNode); expect(node3).toBeInstanceOf(BSTNode); expect(node3).toBeInstanceOf(RedBlackTreeNode); }); - test('forEach should iterate over all elements', () => { + it('forEach should iterate over all elements', () => { const mockCallback = jest.fn(); treeMM.forEach((value, key) => { mockCallback(value, key); @@ -774,7 +774,7 @@ describe('TreeMultiMap iterative methods test', () => { expect(mockCallback.mock.calls[2]).toEqual(['c', 3]); }); - test('filter should return a new tree with filtered elements', () => { + it('filter should return a new tree with filtered elements', () => { const filteredTree = treeMM.filter((value, key) => key > 1); expect(filteredTree.size).toBe(2); expect([...filteredTree]).toEqual([ @@ -783,7 +783,7 @@ describe('TreeMultiMap iterative methods test', () => { ]); }); - test('map should return a new tree with modified elements', () => { + it('map should return a new tree with modified elements', () => { const mappedTree = treeMM.map((value, key) => (key * 2).toString()); expect(mappedTree.size).toBe(3); expect([...mappedTree]).toEqual([ @@ -793,12 +793,12 @@ describe('TreeMultiMap iterative methods test', () => { ]); }); - test('reduce should accumulate values', () => { + it('reduce should accumulate values', () => { const sum = treeMM.reduce((acc, value, key) => acc + key, 0); expect(sum).toBe(6); }); - test('[Symbol.iterator] should provide an iterator', () => { + it('[Symbol.iterator] should provide an iterator', () => { const entries = []; for (const entry of treeMM) { entries.push(entry); @@ -812,7 +812,7 @@ describe('TreeMultiMap iterative methods test', () => { ]); }); - test('should clone work well', () => { + it('should clone work well', () => { expect(treeMM.count).toBe(21); expect(treeMM.getComputedCount()).toBe(21); const cloned = treeMM.clone(); @@ -820,12 +820,12 @@ describe('TreeMultiMap iterative methods test', () => { expect(cloned.root?.right?.value).toBe('c'); }); - test('should keys', () => { + it('should keys', () => { const keys = treeMM.keys(); expect([...keys]).toEqual([1, 2, 3]); }); - test('should values', () => { + it('should values', () => { const values = treeMM.values(); expect([...values]).toEqual(['a', 'b', 'c']); }); diff --git a/test/unit/data-structures/graph/directed-graph.test.ts b/test/unit/data-structures/graph/directed-graph.test.ts index d52e2a7..596e04f 100644 --- a/test/unit/data-structures/graph/directed-graph.test.ts +++ b/test/unit/data-structures/graph/directed-graph.test.ts @@ -139,14 +139,6 @@ class MyDirectedGraph< override createEdge(src: VertexKey, dest: VertexKey, weight?: number, value?: E): EO { return new MyEdge(src, dest, weight ?? 1, value) as EO; } - - setInEdgeMap(value: Map) { - this._inEdgeMap = value; - } - - setOutEdgeMap(value: Map) { - this._outEdgeMap = value; - } } describe('Inherit from DirectedGraph and perform operations', () => { @@ -172,8 +164,8 @@ describe('Inherit from DirectedGraph and perform operations', () => { myGraph.addVertex(2, 'data2'); myGraph.addEdge(1, 2, 10, 'edge-data1-2'); myGraph.addEdge(new MyEdge(2, 1, 20, 'edge-data2-1')); - myGraph.setInEdgeMap(myGraph.inEdgeMap); - myGraph.setOutEdgeMap(myGraph.outEdgeMap); + myGraph.inEdgeMap = myGraph.inEdgeMap; + myGraph.outEdgeMap = myGraph.outEdgeMap; expect(myGraph.edgeSet().length).toBe(2); // TODO @@ -205,11 +197,14 @@ describe('Inherit from DirectedGraph and perform operations', () => { }); it('Remove edge between vertexMap', () => { + expect(myGraph.isEmpty()).toBe(true); myGraph.addVertex(1, 'data1'); myGraph.addVertex(2, 'data2'); myGraph.addEdge(1, 2, 10, 'edge-data1-2'); + expect(myGraph.isEmpty()).toBe(false); const removedEdge = myGraph.deleteEdgeSrcToDest(1, 2); + expect(myGraph.deleteEdgeSrcToDest(2, 10)).toBe(undefined); const edgeAfterRemoval = myGraph.getEdge(1, 2); expect(removedEdge).toBeInstanceOf(MyEdge); @@ -220,6 +215,25 @@ describe('Inherit from DirectedGraph and perform operations', () => { expect(edgeAfterRemoval).toBe(undefined); }); + it('should clear', () => { + expect(myGraph.isEmpty()).toBe(true); + myGraph.addVertex(1, 'data1'); + myGraph.addVertex(2, 'data2'); + myGraph.addEdge(1, 2, 10, 'edge-data1-2'); + expect(myGraph.isEmpty()).toBe(false); + myGraph.clear(); + expect(myGraph.isEmpty()).toBe(true); + }); + + it('should clone', () => { + myGraph.addVertex(1, 'data1'); + myGraph.addVertex(2, 'data2'); + myGraph.addEdge(1, 2, 10, 'edge-data1-2'); + const cloned = myGraph.clone(); + expect(cloned.hasVertex(1)).toBe(true); + expect(cloned.hasEdge(1, 2)).toBe(true); + }); + it('Topological sort', () => { const sorted = myGraph.topologicalSort(); @@ -656,7 +670,7 @@ describe('DirectedGraph iterative Methods', () => { vertexMap.forEach(vertex => graph.addVertex(vertex)); }); - test('[Symbol.iterator] should iterate over all vertexMap', () => { + it('[Symbol.iterator] should iterate over all vertexMap', () => { const iteratedVertices = []; for (const vertex of graph) { iteratedVertices.push(vertex[0]); @@ -664,13 +678,13 @@ describe('DirectedGraph iterative Methods', () => { expect(iteratedVertices).toEqual(vertexMap); }); - test('forEach should apply a function to each vertex', () => { + it('forEach should apply a function to each vertex', () => { const result: VertexKey[] = []; graph.forEach((value, key) => key && result.push(key)); expect(result).toEqual(vertexMap); }); - test('filter should return vertexMap that satisfy the condition', () => { + it('filter should return vertexMap that satisfy the condition', () => { const filtered = graph.filter((value, vertex) => vertex === 'A' || vertex === 'B'); expect(filtered).toEqual([ ['A', undefined], @@ -678,17 +692,17 @@ describe('DirectedGraph iterative Methods', () => { ]); }); - test('map should apply a function to each vertex and return a new array', () => { + 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')); }); - test('reduce should accumulate a value based on each vertex', () => { + it('reduce should accumulate a value based on each vertex', () => { const concatenated = graph.reduce((acc, value, key) => acc + key, ''); expect(concatenated).toBe(vertexMap.join('')); }); - test('Removing an edge of a DirectedGraph should delete additional edges', () => { + it('Removing an edge of a DirectedGraph should delete additional edges', () => { const dg = new DirectedGraph(); dg.addVertex('hello'); dg.addVertex('hi'); @@ -705,7 +719,7 @@ describe('DirectedGraph iterative Methods', () => { expect(dg.incomingEdgesOf('Hi')).toEqual([]); }); - test('Removing a vertex of a DirectedGraph should delete additional edges', () => { + it('Removing a vertex of a DirectedGraph should delete additional edges', () => { const graph = new DirectedGraph(); graph.addVertex('Hello'); @@ -717,7 +731,7 @@ describe('DirectedGraph iterative Methods', () => { expect(graph.incomingEdgesOf('Hi')).toEqual([]); }); - test('Removing a vertex from a DirectedGraph should remove its edges', () => { + it('Removing a vertex from a DirectedGraph should remove its edges', () => { const dg = new DirectedGraph(); dg.addVertex('hello'); dg.addVertex('world'); @@ -740,7 +754,7 @@ describe('DirectedGraph iterative Methods', () => { }); describe('DirectedGraph getCycles', () => { - test('should getCycles return correct result', () => { + it('should getCycles return correct result', () => { const graph = new DirectedGraph(); graph.addVertex('A'); graph.addVertex('B'); @@ -758,7 +772,7 @@ describe('DirectedGraph getCycles', () => { expect(cycles[0]).toEqual(['B', 'D', 'E']); }); - test('should simple cycles graph getCycles return correct result', () => { + it('should simple cycles graph getCycles return correct result', () => { const graph = new DirectedGraph(); graph.addVertex('A'); @@ -779,7 +793,7 @@ describe('DirectedGraph getCycles', () => { ]); }); - test('should 3 cycles graph getCycles return correct result', () => { + it('should 3 cycles graph getCycles return correct result', () => { const graph = new DirectedGraph(); graph.addVertex('A'); @@ -812,7 +826,7 @@ describe('DirectedGraph getCycles', () => { }); describe('DirectedGraph tarjan', () => { - test('should simple cycles graph tarjan cycles return correct result', () => { + it('should simple cycles graph tarjan cycles return correct result', () => { const graph = new DirectedGraph(); graph.addVertex('A'); @@ -853,14 +867,14 @@ describe('DirectedGraph tarjan', () => { return graph; } - test('should tarjan cycles return correct result', () => { + it('should tarjan cycles return correct result', () => { const graph = createExampleGraph1(); const cycles = graph.getCycles(); expect(cycles.length).toBe(1); expect(cycles).toEqual([['B', 'D', 'E']]); }); - test('should tarjan SCCs return correct result', () => { + it('should tarjan SCCs return correct result', () => { const graph = createExampleGraph1(); const sccs = graph.tarjan().SCCs; expect(sccs.size).toBe(3); @@ -878,7 +892,7 @@ describe('DirectedGraph tarjan', () => { return graph; } - test('should 3 cycles graph tarjan cycles return correct result', () => { + it('should 3 cycles graph tarjan cycles return correct result', () => { const graph = createExampleGraph2(); const cycles = graph.getCycles(); expect(cycles.length).toBe(3); @@ -889,7 +903,7 @@ describe('DirectedGraph tarjan', () => { ]); }); - test('should 3 cycles graph tarjan SCCs return correct result', () => { + it('should 3 cycles graph tarjan SCCs return correct result', () => { const graph = createExampleGraph2(); const sccs = graph.tarjan().SCCs; expect(sccs.size).toBe(2); @@ -919,7 +933,7 @@ describe('DirectedGraph tarjan', () => { return graph; } - test('should cuttable graph tarjan cycles return correct result', () => { + it('should cuttable graph tarjan cycles return correct result', () => { const graph = createExampleGraph3(); const cycles = graph.getCycles(); expect(cycles.length).toBe(2); @@ -929,7 +943,7 @@ describe('DirectedGraph tarjan', () => { ]); }); - test('should cuttable graph tarjan SCCs return correct result', () => { + it('should cuttable graph tarjan SCCs return correct result', () => { const graph = createExampleGraph3(); const sccs = graph.tarjan().SCCs; expect(sccs.size).toBe(3); @@ -951,7 +965,7 @@ describe('DirectedGraph tarjan', () => { return graph; } - test('should more cuttable graph tarjan cycles return correct result', () => { + it('should more cuttable graph tarjan cycles return correct result', () => { const graph = createExampleGraph4(); const cycles = graph.getCycles(); expect(cycles.length).toBe(4); @@ -963,7 +977,7 @@ describe('DirectedGraph tarjan', () => { ]); }); - test('should more cuttable graph tarjan SCCs return correct result', () => { + it('should more cuttable graph tarjan SCCs return correct result', () => { const graph = createExampleGraph4(); const sccs = graph.tarjan().SCCs; expect(sccs.size).toBe(3); @@ -976,7 +990,7 @@ describe('DirectedGraph tarjan', () => { return graph; } - test('should uncuttable graph tarjan cycles return correct result', () => { + it('should uncuttable graph tarjan cycles return correct result', () => { const graph = createExampleGraph5(); const cycles = graph.getCycles(); expect(cycles.length).toBe(4); @@ -988,7 +1002,7 @@ describe('DirectedGraph tarjan', () => { ]); }); - test('should uncuttable graph tarjan SCCs return correct result', () => { + it('should uncuttable graph tarjan SCCs return correct result', () => { const graph = createExampleGraph5(); const sccs = graph.tarjan().SCCs; expect(sccs.size).toBe(3); diff --git a/test/unit/data-structures/graph/map-graph.test.ts b/test/unit/data-structures/graph/map-graph.test.ts index 86b57de..4ec95a0 100644 --- a/test/unit/data-structures/graph/map-graph.test.ts +++ b/test/unit/data-structures/graph/map-graph.test.ts @@ -1,8 +1,9 @@ import { MapEdge, MapGraph, MapVertex } from '../../../../src'; describe('MapGraph Operation Test', () => { - it('dijkstra shortest path', () => { - const mapGraph = new MapGraph([5.500338, 100.173665]); + let mapGraph: MapGraph; + beforeEach(() => { + mapGraph = new MapGraph([5.500338, 100.173665]); mapGraph.addVertex(new MapVertex('Surin', '', 5.466724, 100.274805)); mapGraph.addVertex(new MapVertex('Batu Feringgi Beach', '', 5.475141, 100.27667)); @@ -27,6 +28,9 @@ describe('MapGraph Operation Test', () => { mapGraph.addEdge('Trinity Auto', 'Saanen Goat Farm', 26.3); mapGraph.addEdge('The Breeza', 'Penang Airport', 24.8); mapGraph.addEdge('Penang Airport', 'Saanen Goat Farm', 21.2); + }); + + it('dijkstra shortest path', () => { const expected1 = ['Surin', 'Lotus', 'The Breeza', 'Trinity Auto', 'Saanen Goat Farm']; const minPathBetween = mapGraph.getMinPathBetween('Surin', 'Saanen Goat Farm'); @@ -42,6 +46,24 @@ describe('MapGraph Operation Test', () => { expect(surinToSaanenGoatFarmViaDij?.minPath.map(v => v.key)).toEqual(expected2); expect(surinToSaanenGoatFarmViaDij?.minDist).toBe(25.2); }); + + it('should clone', () => { + const cloned = mapGraph.clone(); + const expected1 = ['Surin', 'Lotus', 'The Breeza', 'Trinity Auto', 'Saanen Goat Farm']; + + const minPathBetween = cloned.getMinPathBetween('Surin', 'Saanen Goat Farm'); + expect(minPathBetween?.map(v => v.key)).toEqual(expected1); + const surinToSaanenGoatFarmDij = cloned.dijkstra('Surin', 'Saanen Goat Farm', true, true); + expect(surinToSaanenGoatFarmDij?.minPath.map(v => v.key)).toEqual(expected1); + expect(surinToSaanenGoatFarmDij?.minDist).toBe(41.1); + cloned.addEdge('Surin', 'Batu Feringgi Beach', 1.5); + const expected2 = ['Surin', 'Batu Feringgi Beach', 'Hard Rock Hotel', 'Saanen Goat Farm']; + const minPathBetweenViaBFB = cloned.getMinPathBetween('Surin', 'Saanen Goat Farm', true); + expect(minPathBetweenViaBFB?.map(v => v.key)).toEqual(expected2); + const surinToSaanenGoatFarmViaDij = cloned.dijkstra('Surin', 'Saanen Goat Farm', true, true); + expect(surinToSaanenGoatFarmViaDij?.minPath.map(v => v.key)).toEqual(expected2); + expect(surinToSaanenGoatFarmViaDij?.minDist).toBe(25.2); + }); }); describe('MapGraph', () => { diff --git a/test/unit/data-structures/graph/undirected-graph.test.ts b/test/unit/data-structures/graph/undirected-graph.test.ts index 10660f8..adfed37 100644 --- a/test/unit/data-structures/graph/undirected-graph.test.ts +++ b/test/unit/data-structures/graph/undirected-graph.test.ts @@ -174,7 +174,7 @@ describe('UndirectedGraph', () => { expect(minWeightedPath?.minPath?.[4]?.key).toBe('Intersection_5'); }); - test('Removing an edge of a UndirectedGraph should not delete additional edges', () => { + it('Removing an edge of a UndirectedGraph should not delete additional edges', () => { const dg = new UndirectedGraph(); dg.addVertex('hello'); dg.addVertex('hi'); @@ -190,7 +190,7 @@ describe('UndirectedGraph', () => { expect(dg.getEdge('hello', 'hey')).toBeInstanceOf(UndirectedEdge); }); - test('Removing a vertex of a UndirectedGraph should delete additional edges', () => { + it('Removing a vertex of a UndirectedGraph should delete additional edges', () => { const graph = new UndirectedGraph(); graph.addVertex('Hello'); @@ -202,7 +202,7 @@ describe('UndirectedGraph', () => { expect(graph.edgesOf('Hi')).toEqual([]); }); - test('Removing a vertex from a UndirectedGraph should remove its edges', () => { + it('Removing a vertex from a UndirectedGraph should remove its edges', () => { const dg = new UndirectedGraph(); dg.addVertex('hello'); dg.addVertex('world'); @@ -274,7 +274,7 @@ it('Should return Infinity if dest is not found', () => { }); describe('UndirectedGraph getCycles', () => { - test('should getCycles return correct result', () => { + it('should getCycles return correct result', () => { const graph = new UndirectedGraph(); graph.addVertex('A'); graph.addVertex('B'); @@ -296,7 +296,7 @@ describe('UndirectedGraph getCycles', () => { ]); }); - test('should simple cycles graph getCycles return correct result', () => { + it('should simple cycles graph getCycles return correct result', () => { const graph = new UndirectedGraph(); graph.addVertex('A'); @@ -318,7 +318,7 @@ describe('UndirectedGraph getCycles', () => { ]); }); - test('should 3 cycles graph getCycles return correct result', () => { + it('should 3 cycles graph getCycles return correct result', () => { const graph = new UndirectedGraph(); graph.addVertex('A'); @@ -358,7 +358,7 @@ describe('UndirectedGraph getCycles', () => { }); describe('UndirectedGraph tarjan', () => { - test('should simple cycles graph tarjan cycles return correct result', () => { + it('should simple cycles graph tarjan cycles return correct result', () => { const graph = new UndirectedGraph(); graph.addVertex('A'); @@ -396,38 +396,38 @@ describe('UndirectedGraph tarjan', () => { return graph; } - test('should tarjan cut vertexes return correct result', () => { + it('should tarjan cut vertexes return correct result', () => { const graph = createExampleGraph1(); const cutVertices = graph.tarjan().cutVertices; expect(cutVertices.length).toBe(0); }); - test('should tarjan bridges return correct result', () => { + it('should tarjan bridges return correct result', () => { const graph = createExampleGraph1(); const bridges = graph.tarjan().bridges; expect(bridges.length).toBe(0); }); - test('should 3 cycles graph tarjan cut vertexes return correct result', () => { + it('should 3 cycles graph tarjan cut vertexes return correct result', () => { const graph = createExampleGraph1(); const cutVertices = graph.tarjan().cutVertices; expect(cutVertices.length).toBe(0); }); - test('should 3 cycles graph tarjan bridges return correct result', () => { + it('should 3 cycles graph tarjan bridges return correct result', () => { const graph = createExampleGraph1(); const bridges = graph.tarjan().bridges; expect(bridges.length).toBe(0); }); - test('should cuttable graph tarjan cut vertexes return correct result', () => { + it('should cuttable graph tarjan cut vertexes return correct result', () => { const graph = createExampleGraph3(); const cutVertices = graph.tarjan().cutVertices; expect(cutVertices.length).toBe(3); expect(cutVertices.map(cv => cv.key)).toEqual(['B', 'E', 'A']); }); - test('should cuttable graph tarjan bridges return correct result', () => { + it('should cuttable graph tarjan bridges return correct result', () => { const graph = createExampleGraph3(); const bridges = graph.tarjan().bridges; expect(bridges.length).toBe(2); @@ -437,14 +437,14 @@ describe('UndirectedGraph tarjan', () => { ]); }); - test('should more cuttable graph tarjan cut vertexes return correct result', () => { + it('should more cuttable graph tarjan cut vertexes return correct result', () => { const graph = createExampleGraph4(); const cutVertices = graph.tarjan().cutVertices; expect(cutVertices.length).toBe(4); expect(cutVertices.map(cv => cv.key)).toEqual(['H', 'B', 'E', 'A']); }); - test('should more cuttable graph tarjan bridges return correct result', () => { + it('should more cuttable graph tarjan bridges return correct result', () => { const graph = createExampleGraph4(); const bridges = graph.tarjan().bridges; expect(bridges.length).toBe(2); @@ -454,13 +454,13 @@ describe('UndirectedGraph tarjan', () => { ]); }); - test('should uncuttable graph tarjan cut vertexes return correct result', () => { + it('should uncuttable graph tarjan cut vertexes return correct result', () => { const graph = createExampleGraph5(); const cutVertices = graph.tarjan().cutVertices; expect(cutVertices.length).toBe(1); }); - test('should uncuttable graph tarjan bridges return correct result', () => { + it('should uncuttable graph tarjan bridges return correct result', () => { const graph = createExampleGraph5(); const bridges = graph.tarjan().bridges; expect(bridges.length).toBe(0); @@ -477,7 +477,7 @@ describe('UndirectedGraph tarjan', () => { return graph; } - test('should 3 cycles graph tarjan cycles return correct result', () => { + it('should 3 cycles graph tarjan cycles return correct result', () => { const graph = createExampleGraph2(); const cycles = graph.getCycles(); expect(cycles.length).toBe(10); @@ -515,7 +515,7 @@ describe('UndirectedGraph tarjan', () => { return graph; } - test('should cuttable graph tarjan cycles return correct result', () => { + it('should cuttable graph tarjan cycles return correct result', () => { const graph = createExampleGraph3(); const cycles = graph.getCycles(); expect(cycles.length).toBe(2); @@ -525,7 +525,7 @@ describe('UndirectedGraph tarjan', () => { ]); }); - // test('should cuttable graph tarjan CCs return correct result', () => { + // it('should cuttable graph tarjan CCs return correct result', () => { // const graph = createExampleGraph3(); // const ccs = graph.tarjan().CCs; // expect(ccs.size).toBe(3); @@ -547,7 +547,7 @@ describe('UndirectedGraph tarjan', () => { return graph; } - test('should more cuttable graph tarjan cycles return correct result', () => { + it('should more cuttable graph tarjan cycles return correct result', () => { const graph = createExampleGraph4(); const cycles = graph.getCycles(); expect(cycles.length).toBe(5); @@ -560,7 +560,7 @@ describe('UndirectedGraph tarjan', () => { ]); }); - // test('should more cuttable graph tarjan SCCs return correct result', () => { + // it('should more cuttable graph tarjan SCCs return correct result', () => { // const graph = createExampleGraph4(); // const ccs = graph.tarjan().CCs; // expect(ccs.size).toBe(3); @@ -573,7 +573,7 @@ describe('UndirectedGraph tarjan', () => { return graph; } - test('should uncuttable graph tarjan cycles return correct result', () => { + it('should uncuttable graph tarjan cycles return correct result', () => { const graph = createExampleGraph5(); const cycles = graph.getCycles(); expect(cycles.length).toBe(13); @@ -594,7 +594,7 @@ describe('UndirectedGraph tarjan', () => { ]); }); - // test('should uncuttable graph tarjan SCCs return correct result', () => { + // it('should uncuttable graph tarjan SCCs return correct result', () => { // const graph = createExampleGraph5(); // const ccs = graph.tarjan().CCs; // expect(ccs.size).toBe(3); diff --git a/test/unit/data-structures/hash/hash-map.test.ts b/test/unit/data-structures/hash/hash-map.test.ts index 24d1768..55326a0 100644 --- a/test/unit/data-structures/hash/hash-map.test.ts +++ b/test/unit/data-structures/hash/hash-map.test.ts @@ -13,7 +13,7 @@ describe('HashMap', () => { expect(hashMap.isEmpty()).toBe(true); }); - it('should put and get values', () => { + it('should set and get values', () => { hashMap.set('one', 1); hashMap.set('two', 2); hashMap.set('three', 3); @@ -127,10 +127,11 @@ describe('HashMap', () => { it('should handle object keys correctly', () => { const keyObj = { id: 1 }; hashMap.set(keyObj, 'objectValue'); + expect(hashMap.has(keyObj)).toBe(true); expect(hashMap.get(keyObj)).toBe('objectValue'); }); - test('Inheritability test', () => { + it('Inheritability test', () => { class ExtendedHashMap extends HashMap { someOtherParam?: string; @@ -154,7 +155,7 @@ describe('HashMap', () => { expect(eHM.get('one')).toBe(1); }); - test('should raw elements toEntry', () => { + it('should raw elements toEntry', () => { const rawCollection = [ { id: 1, name: 'item 1' }, { id: 2, name: 'item 2' } @@ -238,17 +239,19 @@ describe('HashMap', () => { describe('HashMap for coordinate object keys', () => { const hashMap: HashMap<[number, number], number> = new HashMap(); const codObjs: [number, number][] = []; - - test('set elements in hash map', () => { - for (let i = 0; i < 1000; i++) { - const codObj: [number, number] = [getRandomInt(-10000, 10000), i]; - codObjs.push(codObj); + for (let i = 0; i < 10; i++) { + const codObj: [number, number] = [getRandomInt(-10000, 10000), i]; + codObjs.push(codObj); + } + it('set elements in hash map', () => { + for (let i = 0; i < codObjs.length; i++) { + const codObj = codObjs[i]; hashMap.set(codObj, i); } }); - test('get elements in hash map', () => { - for (let i = 0; i < 1000; i++) { + it('get elements in hash map', () => { + for (let i = 0; i < codObjs.length; i++) { const codObj = codObjs[i]; if (codObj) { expect(hashMap.get(codObj)).toBe(i); @@ -256,8 +259,12 @@ describe('HashMap', () => { } }); - test('delete elements in hash map', () => { - for (let i = 0; i < 1000; i++) { + it('should spread elements in hash map', () => { + expect([...hashMap]).toEqual(codObjs.map(codObj => [codObj, codObj[1]])); + }); + + it('delete elements in hash map', () => { + for (let i = 0; i < 10; i++) { if (i === 500) expect(hashMap.size).toBe(500); const codObj = codObjs[i]; if (codObj) hashMap.delete(codObj); @@ -285,11 +292,11 @@ describe('HashMap', () => { ]); }); - test('keys', () => { + it('keys', () => { expect([...hm.keys()]).toEqual([2, 3, 4, 5, 6]); }); - test('values', () => { + it('values', () => { expect([...hm.values()]).toEqual([2, 3, 4, 5, 6]); }); }); @@ -304,31 +311,31 @@ describe('HashMap', () => { hashMap.set('key3', 'value3'); }); - test('every() returns true if all elements match the condition', () => { + it('every() returns true if all elements match the condition', () => { expect(hashMap.every(value => typeof value === 'string')).toBe(true); }); - test('some() returns true if any element matches the condition', () => { + it('some() returns true if any element matches the condition', () => { expect(hashMap.some((value, key) => key === 'key1')).toBe(true); }); - test('forEach() should execute a function for each element', () => { + it('forEach() should execute a function for each element', () => { const mockCallback = jest.fn(); hashMap.forEach(mockCallback); expect(mockCallback.mock.calls.length).toBe(3); }); - test('map() should transform each element', () => { + it('map() should transform each element', () => { const newHashMap = hashMap.map(value => value.toUpperCase()); expect(newHashMap.get('key1')).toBe('VALUE1'); }); - test('filter() should remove elements that do not match the condition', () => { + it('filter() should remove elements that do not match the condition', () => { const filteredHashMap = hashMap.filter((value, key) => key !== 'key1'); expect(filteredHashMap.has('key1')).toBe(false); }); - test('reduce() should accumulate values', () => { + it('reduce() should accumulate values', () => { const result = hashMap.reduce((acc, value) => acc + value, ''); expect(result).toBe('value1value2value3'); }); @@ -347,6 +354,7 @@ describe('LinkedHashMap', () => { }); it('should add a key-value pair', () => { + expect(hashMap.first).toBe(undefined); hashMap.set('key1', 'value1'); expect(hashMap.get('key1')).toBe('value1'); }); @@ -441,29 +449,99 @@ describe('LinkedHashMap', () => { compareHashMaps(hashMap, stdMap); }); - test('should iterate correctly with reverse iterators', () => { + it('should iterate correctly with reverse iterators', () => { hashMap.set('key1', 'value1'); hashMap.set('key2', 'value2'); const iterator = hashMap.reverseBegin(); expect(iterator.next().value).toEqual(['key2', 'value2']); }); - test('should return the last element', () => { + it('should return the last element', () => { hashMap.set('key1', 'value1'); hashMap.set('key2', 'value2'); expect(hashMap.last).toEqual(['key2', 'value2']); }); - test('should return undefined for empty map', () => { + it('should return undefined for empty map', () => { expect(hashMap.last).toBeUndefined(); }); - test('should get element at specific index', () => { + it('should get element at specific index', () => { hashMap.set('key1', 'value1'); hashMap.set('key2', 'value2'); expect(hashMap.at(1)).toBe('value2'); }); + it('should hashFn, objHashFn, toEntryFn work well', () => { + const data: Array<{ name: number }> = [{ name: 1 }, { name: 2 }, { name: 3 }]; + const hm = new LinkedHashMap(data, { + hashFn: key => String(key), + objHashFn: obj => obj, + toEntryFn: ({ name }) => [{ name }, name] + }); + + expect(hm.hashFn).toBeTruthy(); + expect(hm.objHashFn).toBeTruthy(); + expect(hm.toEntryFn).toBeTruthy(); + expect([...hm]).toEqual([ + [{ name: 1 }, 1], + [{ name: 2 }, 2], + [{ name: 3 }, 3] + ]); + }); + + it('should begin, reverseBegin', () => { + const data: Array<{ name: number }> = [{ name: 1 }, { name: 2 }, { name: 3 }]; + const hm = new LinkedHashMap(data, { + hashFn: key => String(key), + objHashFn: obj => obj, + toEntryFn: ({ name }) => [{ name }, name] + }); + + expect(hm.begin().next()).toEqual({ + done: false, + value: [ + { + name: 1 + }, + 1 + ] + }); + + expect(hm.reverseBegin().next()).toEqual({ + done: false, + value: [ + { + name: 3 + }, + 3 + ] + }); + }); + + it('should clone', () => { + hashMap = new LinkedHashMap(); + + hashMap.set('one', 1); + hashMap.set('two', 2); + for (let i = 3; i <= 100; i++) { + hashMap.set(i.toString(), i); + } + + expect(hashMap.get('one')).toBe(1); + expect(hashMap.get('two')).toBe(2); + expect(hashMap.get('86')).toBe(86); + expect(hashMap.size).toBe(100); + hashMap.delete('two'); + expect(hashMap.size).toBe(99); + + const cloned = hashMap.clone(); + expect(cloned.get('one')).toBe(1); + expect(cloned.get('two')).toBe(undefined); + expect(cloned.get('86')).toBe(86); + expect(cloned.size).toBe(99); + }); + describe('LinkedHashMap basic', () => { let hashMap: LinkedHashMap; @@ -506,6 +584,9 @@ describe('LinkedHashMap', () => { hashMap.delete('one'); expect(hashMap.get('one')).toBeUndefined(); expect(hashMap.size).toBe(1); + hashMap.deleteAt(0); + // expect(hashMap.get('two')).toBe(undefined); // TODO #99 + expect(hashMap.size).toBe(0); }); it('should clear the LinkedHashMap', () => { @@ -570,7 +651,7 @@ describe('LinkedHashMap', () => { const hashMap: LinkedHashMap<[number, number], number> = new LinkedHashMap(); const codObjs: [number, number][] = []; - test('set elements in hash map', () => { + it('set elements in hash map', () => { for (let i = 0; i < 1000; i++) { const codObj: [number, number] = [getRandomInt(-10000, 10000), i]; codObjs.push(codObj); @@ -578,7 +659,7 @@ describe('LinkedHashMap', () => { } }); - test('get elements in hash map', () => { + it('get elements in hash map', () => { for (let i = 0; i < 1000; i++) { const codObj = codObjs[i]; if (codObj) { @@ -587,7 +668,7 @@ describe('LinkedHashMap', () => { } }); - test('delete elements in hash map', () => { + it('delete elements in hash map', () => { for (let i = 0; i < 1000; i++) { if (i === 500) expect(hashMap.size).toBe(500); const codObj = codObjs[i]; @@ -616,15 +697,15 @@ describe('LinkedHashMap', () => { ]); }); - test('keys', () => { + it('keys', () => { expect([...hm.keys()]).toEqual([2, 3, 4, 5, 6]); }); - test('values', () => { + it('values', () => { expect([...hm.values()]).toEqual([2, 3, 4, 5, 6]); }); - test('entries', () => { + it('entries', () => { expect([...hm.entries()]).toEqual([ [2, 2], [3, 3], @@ -634,21 +715,61 @@ describe('LinkedHashMap', () => { ]); }); - test('every', () => { + it('every', () => { expect(hm.every(value => value > 4)).toBe(false); }); - test('some', () => { + it('some', () => { expect(hm.some(value => value > 6)).toBe(false); }); - test('hasValue', () => { + it('hasValue', () => { expect(hm.hasValue(3)).toBe(true); expect(hm.hasValue(7)).toBe(false); }); - test('print', () => { + it('print', () => { // hm.print(); }); }); + + describe('HashMap HOF', () => { + let hashMap: LinkedHashMap; + + beforeEach(() => { + hashMap = new LinkedHashMap(); + hashMap.set('key1', 'value1'); + hashMap.set('key2', 'value2'); + hashMap.set('key3', 'value3'); + }); + + it('every() returns true if all elements match the condition', () => { + expect(hashMap.every(value => typeof value === 'string')).toBe(true); + }); + + it('some() returns true if any element matches the condition', () => { + expect(hashMap.some((value, key) => key === 'key1')).toBe(true); + }); + + it('forEach() should execute a function for each element', () => { + const mockCallback = jest.fn(); + hashMap.forEach(mockCallback); + expect(mockCallback.mock.calls.length).toBe(3); + }); + + it('map() should transform each element', () => { + const newHashMap = hashMap.map(value => value.toUpperCase()); + expect(newHashMap.get('key1')).toBe('VALUE1'); + }); + + it('filter() should remove elements that do not match the condition', () => { + const filteredHashMap = hashMap.filter((value, key) => key !== 'key1'); + expect(filteredHashMap.has('key1')).toBe(false); + }); + + it('reduce() should accumulate values', () => { + const result = hashMap.reduce((acc, value) => acc + value, ''); + expect(result).toBe('value1value2value3'); + }); + }); }); diff --git a/test/unit/data-structures/heap/heap.test.ts b/test/unit/data-structures/heap/heap.test.ts index 9fdcb73..3a0a116 100644 --- a/test/unit/data-structures/heap/heap.test.ts +++ b/test/unit/data-structures/heap/heap.test.ts @@ -2,9 +2,15 @@ import { FibonacciHeap, Heap, MaxHeap, MinHeap } from '../../../../src'; import { logBigOMetricsWrap } from '../../../utils'; describe('Heap Operation Test', () => { + it('should initiate heap', function () { + const hp = new Heap(); + hp.add(1); + expect(hp.size).toBe(1); + }); + it('should heap add and delete work well', function () { const hp = new MinHeap(); - + expect(hp.delete(1)).toBe(false); hp.add(2); hp.add(3); hp.add(1); @@ -23,6 +29,7 @@ describe('Heap Operation Test', () => { it('should numeric heap work well', function () { const minNumHeap = new MinHeap(); + expect(minNumHeap.poll()).toBe(undefined); minNumHeap.add(1); minNumHeap.add(6); minNumHeap.add(2); @@ -219,8 +226,47 @@ describe('Heap Operation Test', () => { expect([...heap.sort()]).toEqual([1, 3, 4, 5, 6, 7, 8]); }); + + it('should getter leaf', function () { + const hp = new Heap(); + expect(hp.leaf).toBe(undefined); + hp.add(1); + expect(hp.leaf).toBe(1); + }); + + it('should error', function () { + expect(() => { + new Heap([{ key: 1 }, { key: 2 }, { key: 3 }]); + }).toThrow( + "When comparing object types, a custom comparator must be defined in the constructor's options parameter." + ); + }); }); +describe('Heap HOF', () => { + let hp: Heap; + + beforeEach(() => { + hp = new Heap([{ key: 1 }, { key: 2 }, { key: 3 }], { comparator: (a, b) => a.key - b.key }); + }); + + it('should filter', () => { + const filtered = hp.filter(({ key }) => key % 2 === 1); + expect([...filtered]).toEqual([{ key: 1 }, { key: 3 }]); + }); + + it('should map', () => { + const mapped = hp.map( + ({ key }) => [key, key], + (a, b) => a[0] - b[0] + ); + expect([...mapped]).toEqual([ + [1, 1], + [2, 2], + [3, 3] + ]); + }); +}); describe('FibonacciHeap', () => { let heap: FibonacciHeap; @@ -411,41 +457,3 @@ describe('FibonacciHeap Stress Test', () => { ); }); }); - -// describe('Competitor performance compare', () => { -// const minHeap = new MinHeap(); -// const cHeap = new CHeap(); -// const cPQ = new PriorityQueue(undefined, (a, b) => a - b); -// const n = 100000; -// -// it('should add performance well', () => { -// const heapCost = calcRunTime(() => { -// for (let i = 0; i < n; i++) { -// minHeap.add(i); -// } -// }) -// -// console.log(`heapCost: ${heapCost}`) -// }); -// -// it('should add performance well', () => { -// -// const cHeapCost = calcRunTime(() => { -// for (let i = 0; i < n; i++) { -// cHeap.push(i); -// } -// }) -// -// console.log(`cHeapCost: ${cHeapCost}`) -// }); -// -// it('should add performance well', () => { -// -// const cPQCost = calcRunTime(() => { -// for (let i = 0; i < n; i++) { -// cPQ.push(i); -// } -// }) -// console.log(`cPQCost: ${cPQCost}`) -// }); -// }); diff --git a/test/unit/data-structures/heap/min-heap.test.ts b/test/unit/data-structures/heap/min-heap.test.ts index 0478618..5fe9e8a 100644 --- a/test/unit/data-structures/heap/min-heap.test.ts +++ b/test/unit/data-structures/heap/min-heap.test.ts @@ -90,22 +90,22 @@ describe('Heap iterative methods', () => { } }); - test('Heap is iterable', () => { + it('Heap is iterable', () => { expect([...heap]).toEqual([10, 20, 30, 40, 50, 60, 70, 80, 90, 100]); }); - test('forEach method calls a function for each element', () => { + it('forEach method calls a function for each element', () => { const mockCallback = jest.fn(); heap.forEach(mockCallback); expect(mockCallback.mock.calls.length).toBe(10); }); - test('filter method returns filtered elements', () => { + it('filter method returns filtered elements', () => { const result = heap.filter(x => x > 50); expect([...result]).toEqual([60, 70, 80, 90, 100]); }); - test('map method correctly maps elements', () => { + it('map method correctly maps elements', () => { const result = heap.map( x => x / 10, (a: number, b: number) => a - b @@ -113,7 +113,7 @@ describe('Heap iterative methods', () => { expect([...result]).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); }); - test('reduce method correctly reduces elements', () => { + it('reduce method correctly reduces elements', () => { const result = heap.reduce((acc, curr) => acc + curr, 0); expect(result).toBe(550); // 10+20+...+100 = 550 }); diff --git a/test/unit/data-structures/linked-list/doubly-linked-list.test.ts b/test/unit/data-structures/linked-list/doubly-linked-list.test.ts index f152ea5..dd8fbc9 100644 --- a/test/unit/data-structures/linked-list/doubly-linked-list.test.ts +++ b/test/unit/data-structures/linked-list/doubly-linked-list.test.ts @@ -20,14 +20,27 @@ describe('DoublyLinkedList Operation Test', () => { expect(list.getNodeAt(-1)).toBe(undefined); expect(list.getNodeAt(5)).toBe(undefined); expect(list.addAt(5, 6)).toBe(true); + expect(list.addAt(-1, 6)).toBe(false); + expect(list.addAt(7, 6)).toBe(false); + expect(list.addAt(100, 6)).toBe(false); }); it('should addBefore', () => { expect(list.addBefore(1, 0)).toBe(true); + expect(list.addBefore(list.getNode(1)!, 2)).toBe(true); + expect([...list]).toEqual([0, 2, 1, 2, 3, 4, 5]); }); it('should deleteAt', () => { - expect(list.deleteAt(1)).toBeTruthy(); + expect(list.deleteAt(1)).toBe(true); + expect(list.deleteAt(-1)).toBe(false); + expect(list.deleteAt(list.size)).toBe(false); + expect(list.size).toBe(4); + expect(list.deleteAt(4)).toBe(false); + expect([...list]).toEqual([1, 3, 4, 5]); + expect(list.isEmpty()).toBe(false); + expect(list.deleteAt(3)).toBe(true); + expect([...list]).toEqual([1, 3, 4]); }); it('should delete tail', () => { @@ -94,6 +107,13 @@ describe('DoublyLinkedList Operation Test', () => { expect(list.tail).toBe(undefined); }); + it('should initialize with toElementFn', () => { + const dl = new DoublyLinkedList([{ key: 1 }, { key: 2 }, { key: 3 }], { toElementFn: ({ key }) => key }); + expect([...dl]).toEqual([1, 2, 3]); + expect(dl.first).toBe(1); + expect(dl.last).toBe(3); + }); + it('should push elements to the list', () => { list.push(1); list.push(2); @@ -111,8 +131,13 @@ describe('DoublyLinkedList Operation Test', () => { expect(list.size).toBe(1); expect(list.head!.value).toBe(1); expect(list.tail!.value).toBe(1); + list.pop(); + expect([...list]).toEqual([]); + list.pop(); + expect([...list]).toEqual([]); }); it('should insert elements at specific positions', () => { + expect(list.at(0)).toBe(undefined); list.push(1); list.push(2); list.push(3); @@ -134,6 +159,8 @@ describe('DoublyLinkedList Operation Test', () => { expect(list.size).toBe(6); expect(list.at(5)).toBe(4); expect(list.tail!.value).toBe(4); + expect(list.at(-1)).toBe(undefined); + expect(list.at(6)).toBe(undefined); }); it('should delete elements at specific positions', () => { @@ -445,9 +472,11 @@ describe('iterable methods', () => { expect(dl.reduce((accumulator, element) => accumulator + element, 0)).toEqual(6); }); - test('values', () => { + it('values', () => { const dl = new DoublyLinkedList(); - dl.push(1); + dl.shift(); + expect([...dl]).toEqual([]); + dl.unshift(1); dl.push(2); dl.push(3); dl.delete(2); @@ -458,7 +487,7 @@ describe('iterable methods', () => { expect([...dl.values()]).toEqual([3, 1]); }); - test('some', () => { + it('some', () => { const dl = new DoublyLinkedList(); dl.push(1); dl.push(2); diff --git a/test/unit/data-structures/linked-list/singly-linked-list.test.ts b/test/unit/data-structures/linked-list/singly-linked-list.test.ts index 975acb7..1e22481 100644 --- a/test/unit/data-structures/linked-list/singly-linked-list.test.ts +++ b/test/unit/data-structures/linked-list/singly-linked-list.test.ts @@ -9,6 +9,16 @@ describe('SinglyLinkedListNode', () => { }); }); +describe('SinglyLinkedList Initiate Test', () => { + it('should initiate with toElementFn', () => { + const sl = new SinglyLinkedList([{ key: 1 }, { key: 2 }, { key: 3 }, { key: 4 }, { key: 5 }], { + toElementFn: ({ key }) => key + }); + + expect([...sl]).toEqual([1, 2, 3, 4, 5]); + }); +}); + describe('SinglyLinkedList Operation Test', () => { let list: SinglyLinkedList; let objectList: SinglyLinkedList<{ @@ -95,6 +105,10 @@ describe('SinglyLinkedList Operation Test', () => { list.push(3); list.addAfter(2, 4); expect(list.toArray()).toEqual([1, 2, 4, 3]); + list.addAfter(list.getNode(2)!, 4); + expect(list.toArray()).toEqual([1, 2, 4, 4, 3]); + list.addAfter(list.getNode(3)!, 4); + expect(list.toArray()).toEqual([1, 2, 4, 4, 3, 4]); }); it('should return false if the existing value is not found', () => { @@ -132,12 +146,17 @@ describe('SinglyLinkedList Operation Test', () => { list.push(3); list.push(4); list.push(5); + expect(list.delete(undefined)).toBe(false); expect(list.delete(2)).toBe(true); expect(list.toArray()).toEqual([1, 3, 4, 5]); expect(list.delete(1)).toBe(true); expect(list.toArray()).toEqual([3, 4, 5]); expect(list.delete(5)).toBe(true); expect(list.toArray()).toEqual([3, 4]); + expect(list.delete(4)).toBe(true); + expect(list.toArray()).toEqual([3]); + expect(list.delete(3)).toBe(true); + expect(list.toArray()).toEqual([]); }); it('should return false if the value is not found', () => { @@ -229,11 +248,14 @@ describe('SinglyLinkedList Operation Test', () => { describe('addBefore', () => { it('should insert an element before an existing value', () => { + expect(list.addBefore(2, 4)).toBe(false); list.push(1); list.push(2); list.push(3); list.addBefore(2, 4); expect(list.toArray()).toEqual([1, 4, 2, 3]); + expect(list.addBefore(list.getNode(2)!, 4)).toBe(true); + expect([...list]).toEqual([1, 4, 4, 2, 3]); }); it('should insert an element at the beginning', () => { @@ -281,7 +303,11 @@ describe('SinglyLinkedList Operation Test', () => { it('should delete and return the first element', () => { list.push(1); list.push(2); + expect(list.first).toBe(1); + expect(list.last).toBe(2); const removed = list.deleteAt(0); + expect(list.first).toBe(2); + expect(list.last).toBe(2); expect(removed).toBe(true); expect(list.toArray()).toEqual([2]); }); @@ -321,6 +347,9 @@ describe('SinglyLinkedList Operation Test', () => { list.addAt(1, 3); list.addAt(1, 2); expect(list.toArray()).toEqual([1, 2, 3]); + expect(list.addAt(5, 5)).toBe(false); + expect(list.addAt(-1, -1)).toBe(false); + expect(list.toArray()).toEqual([1, 2, 3]); }); }); @@ -354,6 +383,7 @@ describe('SinglyLinkedList Operation Test', () => { it('should clone', function () { const sList = new SinglyLinkedList(); + sList.delete('1'); sList.push('1'); sList.push('6'); sList.push('2'); @@ -367,6 +397,8 @@ describe('SinglyLinkedList Operation Test', () => { sList.delete('5'); expect([...sList]).toEqual(['1', '6', '0', '9']); expect([...cloned]).toEqual(['1', '6', '0', '5', '9']); + sList.delete(sList.getNode('0')); + expect([...sList]).toEqual(['1', '6', '9']); }); describe('countOccurrences', () => { diff --git a/test/unit/data-structures/matrix/matrix.test.ts b/test/unit/data-structures/matrix/matrix.test.ts index 7e99e3a..161ba97 100644 --- a/test/unit/data-structures/matrix/matrix.test.ts +++ b/test/unit/data-structures/matrix/matrix.test.ts @@ -22,6 +22,14 @@ describe('Matrix', () => { ]); }); + it('should initiate with empty', () => { + matrix = new Matrix([], { rows: 2, cols: 2 }); + expect(matrix.data).toEqual([ + [0, 0], + [0, 0] + ]); + }); + it('should get a value at a specific position', () => { expect(matrix.get(1, 1)).toBe(0); expect(matrix.get(2, 2)).toBe(0); @@ -213,7 +221,7 @@ describe('Matrix', () => { }); describe('transpose', () => { - test('should transpose a matrix with numeric values correctly', () => { + it('should transpose a matrix with numeric values correctly', () => { const originalMatrix = new Matrix([ [1, 2, 3], [4, 5, 6], @@ -231,7 +239,7 @@ describe('Matrix', () => { ]); }); - test('should transpose an empty matrix correctly', () => { + it('should transpose an empty matrix correctly', () => { const originalMatrix = new Matrix([]); const transposedMatrix = originalMatrix.transpose(); @@ -241,7 +249,7 @@ describe('Matrix', () => { expect(transposedMatrix.data).toEqual([]); }); - test('should throw an error when transposing a non-rectangular matrix', () => { + it('should throw an error when transposing a non-rectangular matrix', () => { const originalMatrix = new Matrix([ [1, 2, 3], [4, 5] @@ -292,10 +300,32 @@ describe('Matrix', () => { [-0.046511627906976744, -0.023255813953488372, 0.23255813953488372] ]); }); + + it('should throw error when cols does not equal to rows', () => { + const data: number[][] = [ + [4, 7, 2], + [2, 6, 3] + ]; + + const matrix = new Matrix(data); + expect(() => matrix.inverse()).toThrow('Matrix must be square for inversion.'); + }); + + it('should clone', () => { + const data: number[][] = [ + [4, 7, 2], + [2, 6, 3] + ]; + + const matrix = new Matrix(data); + const cloned = matrix.clone(); + expect(cloned instanceof Matrix).toBe(true); + expect(cloned.data).toEqual(data); + }); }); describe('dot', () => { - test('should calculate the dot product of two matrices', () => { + it('should calculate the dot product of two matrices', () => { const matrix1 = new Matrix([ [1, 2], [3, 4] @@ -313,7 +343,7 @@ describe('Matrix', () => { ]); }); - test('should throw an error for incompatible matrices', () => { + it('should throw an error for incompatible matrices', () => { const matrix1 = new Matrix([ [1, 2], [3, 4] diff --git a/test/unit/data-structures/priority-queue/max-priority-queue.test.ts b/test/unit/data-structures/priority-queue/max-priority-queue.test.ts index feea1b4..e5b2b79 100644 --- a/test/unit/data-structures/priority-queue/max-priority-queue.test.ts +++ b/test/unit/data-structures/priority-queue/max-priority-queue.test.ts @@ -129,4 +129,35 @@ describe('MaxPriorityQueue Operation Test', () => { expect([...maxPQ.sort()]).toEqual([8, 7, 6, 5, 4, 3, 1]); }); + + it('should MaxPriorityQueue filter, map work well', function () { + const minPQ2 = new MaxPriorityQueue([]); + minPQ2.refill([2, 5, 8, 1, 6, 7, 4]); + + const cloned = minPQ2.clone(); + const filtered = cloned.filter(item => item % 2 === 1); + expect(filtered instanceof MaxPriorityQueue).toBe(true); + expect([...filtered]).toEqual([7, 1, 5]); + + const mapped = filtered.map( + item => ({ key: item }), + (a, b) => b.key - a.key + ); + expect(mapped instanceof MaxPriorityQueue).toBe(true); + expect([...mapped]).toEqual([{ key: 7 }, { key: 1 }, { key: 5 }]); + }); + + it('should MaxPriorityQueue throw an error while initialed with object data', function () { + expect(() => { + new MaxPriorityQueue<{ key: number }>([{ key: 7 }, { key: 1 }, { key: 7 }]); + }).toThrow( + "When comparing object types, a custom comparator must be defined in the constructor's options parameter." + ); + }); + + it('should MaxPriorityQueue comparator return 0 when equal values are added', function () { + const duplicated = new MaxPriorityQueue([7, 1, 7, 7]); + expect(duplicated.size).toBe(4); + expect([...duplicated]).toEqual([7, 7, 7, 1]); + }); }); diff --git a/test/unit/data-structures/priority-queue/min-priority-queue.test.ts b/test/unit/data-structures/priority-queue/min-priority-queue.test.ts index 1daae1b..1698e76 100644 --- a/test/unit/data-structures/priority-queue/min-priority-queue.test.ts +++ b/test/unit/data-structures/priority-queue/min-priority-queue.test.ts @@ -60,4 +60,21 @@ describe('MinPriorityQueue Operation Test', () => { const sortedArray = priorityQueue.sort(); expect(sortedArray).toEqual([1, 3, 5, 7]); }); + + it('should MinPriorityQueue filter, map work well', function () { + const minPQ2 = new MinPriorityQueue([]); + minPQ2.refill([2, 5, 8, 1, 6, 7, 4]); + + const cloned = minPQ2.clone(); + const filtered = cloned.filter(item => item % 2 === 1); + expect(filtered instanceof MinPriorityQueue).toBe(true); + expect([...filtered]).toEqual([1, 5, 7]); + + const mapped = filtered.map( + item => ({ key: item }), + (a, b) => a.key - b.key + ); + expect(mapped instanceof MinPriorityQueue).toBe(true); + expect([...mapped]).toEqual([{ key: 1 }, { key: 5 }, { key: 7 }]); + }); }); diff --git a/test/unit/data-structures/priority-queue/priority-queue.test.ts b/test/unit/data-structures/priority-queue/priority-queue.test.ts index 8e4a2fd..6e10580 100644 --- a/test/unit/data-structures/priority-queue/priority-queue.test.ts +++ b/test/unit/data-structures/priority-queue/priority-queue.test.ts @@ -52,6 +52,23 @@ describe('PriorityQueue Operation Test', () => { expect(minPQ1.dfs('POST')).toEqual([4, 3, 5, 2, 8, 7, 6, 1]); expect(minPQ1.dfs('PRE')).toEqual([1, 2, 3, 4, 5, 6, 8, 7]); }); + + it('should PriorityQueue filter, map work well', function () { + const minPQ2 = new PriorityQueue([], { + comparator: (a, b) => a - b + }); + minPQ2.refill([2, 5, 8, 3, 1, 6, 7, 4]); + const filtered = minPQ2.filter(item => item % 2 === 1); + expect(filtered instanceof PriorityQueue).toBe(true); + expect([...filtered]).toEqual([1, 3, 5, 7]); + + const mapped = filtered.map( + item => ({ key: item }), + (a, b) => a.key - b.key + ); + expect(mapped instanceof PriorityQueue).toBe(true); + expect([...mapped]).toEqual([{ key: 1 }, { key: 3 }, { key: 5 }, { key: 7 }]); + }); }); describe('Priority Queue Performance Test', () => { diff --git a/test/unit/data-structures/queue/deque.test.ts b/test/unit/data-structures/queue/deque.test.ts index 257ec1a..ed86eb9 100644 --- a/test/unit/data-structures/queue/deque.test.ts +++ b/test/unit/data-structures/queue/deque.test.ts @@ -10,19 +10,19 @@ describe('Deque - Basic Operations', () => { deque = new Deque([1, 2]); }); - test('push should add elements to the end', () => { + it('push should add elements to the end', () => { expect(deque.size).toBe(2); expect(deque.last).toBe(2); }); - test('pop should remove elements from the end', () => { + it('pop should remove elements from the end', () => { expect(deque.pop()).toBe(2); expect(deque.size).toBe(1); expect(deque.pop()).toBe(1); expect(deque.isEmpty()).toBeTruthy(); }); - test('unshift should add elements to the beginning', () => { + it('unshift should add elements to the beginning', () => { deque.clear(); deque.unshift(1); deque.unshift(2); @@ -30,7 +30,7 @@ describe('Deque - Basic Operations', () => { expect(deque.first).toBe(2); }); - test('shift should remove elements from the beginning', () => { + it('shift should remove elements from the beginning', () => { deque.clear(); deque.unshift(1); deque.unshift(2); @@ -40,17 +40,17 @@ describe('Deque - Basic Operations', () => { expect(deque.isEmpty()).toBeTruthy(); }); - test('at should retrieve the correct element', () => { + it('at should retrieve the correct element', () => { expect(deque.at(0)).toBe(1); expect(deque.at(1)).toBe(2); }); - test('setAt should set the correct element', () => { + it('setAt should set the correct element', () => { deque.setAt(0, 3); expect(deque.at(0)).toBe(3); }); - test('should at after shifting', () => { + it('should at after shifting', () => { deque.clear(); for (let i = 0; i < 100; i++) { deque.push(i); @@ -65,7 +65,7 @@ describe('Deque - Basic Operations', () => { } }); - test('should at after popping', () => { + it('should at after popping', () => { deque.clear(); for (let i = 0; i < 100; i++) { deque.push(i); @@ -115,14 +115,14 @@ describe('Deque - Complex Operations', () => { deque = new Deque(); }); - test('addAt should insert elements at the specified position', () => { - deque.push(1); - deque.push(3); + it('addAt should insert elements at the specified position', () => { + deque.addAt(0, 1); + deque.addAt(1, 3); deque.addAt(1, 2); expect(deque.toArray()).toEqual([1, 2, 3]); }); - test('cut should remove elements after the specified position', () => { + it('cut should remove elements after the specified position', () => { deque.push(1); deque.push(2); deque.push(3); @@ -136,9 +136,17 @@ describe('Deque - Complex Operations', () => { expect([...dq1.cut(3, true)]).toEqual([1, 2, 3, 4]); expect(dq1.size).toBe(4); expect([...dq1]).toEqual([1, 2, 3, 4]); + const dqCut = dq1.cut(2); + expect(dqCut.toArray()).toEqual([1, 2, 3]); + const dqCutFromBeginning = dqCut.cut(0, true); + expect(dqCutFromBeginning.toArray()).toEqual([1]); + dqCutFromBeginning.cut(-1, true); + expect(dqCutFromBeginning.toArray()).toEqual([]); + const dqCutFromNegative = dqCutFromBeginning.cut(-1); + expect([...dqCutFromNegative]).toEqual([]); }); - test('cutRest should remove elements after the specified position', () => { + it('cutRest should remove elements after the specified position', () => { deque.push(1); deque.push(2); deque.push(3); @@ -160,17 +168,28 @@ describe('Deque - Complex Operations', () => { const dq1 = new Deque([1, 2, 3, 4, 5, 6, 7]); expect([...dq1.cutRest(3)]).toEqual([4, 5, 6, 7]); expect([...dq1]).toEqual([1, 2, 3, 4, 5, 6, 7]); + const dq2 = dq1.cutRest(0, true); + expect(dq2.toArray()).toEqual([1, 2, 3, 4, 5, 6, 7]); + dq2.cutRest(-1, true); + expect(dq2.toArray()).toEqual([1, 2, 3, 4, 5, 6, 7]); + const dq3 = dq2.cutRest(-1); + expect([...dq3]).toEqual([1, 2, 3, 4, 5, 6, 7]); }); - test('deleteAt should remove the element at the specified position', () => { + it('deleteAt should remove the element at the specified position', () => { deque.push(1); deque.push(2); deque.push(3); deque.deleteAt(1); expect(deque.toArray()).toEqual([1, 3]); + deque.deleteAt(1); + deque.deleteAt(0); + expect(deque.toArray()).toEqual([]); }); - test('delete should remove all instances of an element', () => { + it('delete should remove all instances of an element', () => { + deque.delete(2); + expect(deque.toArray()).toEqual([]); deque.push(1); deque.push(2); deque.push(2); @@ -179,7 +198,7 @@ describe('Deque - Complex Operations', () => { expect(deque.toArray()).toEqual([1, 3]); }); - test('reverse should reverse the order of elements', () => { + it('reverse should reverse the order of elements', () => { deque.push(1); deque.push(2); deque.push(3); @@ -187,16 +206,19 @@ describe('Deque - Complex Operations', () => { expect(deque.toArray()).toEqual([3, 2, 1]); }); - test('unique should remove duplicate elements', () => { + it('unique should remove duplicate elements', () => { deque.push(1); + const noNeedUnique = deque.unique(); + expect(noNeedUnique).toBe(deque); deque.push(2); deque.push(2); deque.push(3); - deque.unique(); + const uniquer = deque.unique(); + expect(uniquer).toBe(deque); expect(deque.toArray()).toEqual([1, 2, 3]); }); - test('sort should sort elements according to a comparator', () => { + it('sort should sort elements according to a comparator', () => { deque.push(3); deque.push(1); deque.push(2); @@ -204,7 +226,43 @@ describe('Deque - Complex Operations', () => { expect([...deque]).toEqual([1, 2, 3]); }); - test('shrinkToFit should reduce the memory footprint', () => {}); + it('shrinkToFit should reduce the memory footprint', () => { + deque.shrinkToFit(); + expect(deque.size).toBe(0); + expect(deque.has(1)).toBe(false); + expect(deque.bucketFirst).toBe(0); + expect(deque.bucketLast).toBe(0); + expect(deque.firstInBucket).toBe(2048); + expect(deque.lastInBucket).toBe(2048); + expect(deque.bucketCount).toBe(1); + expect(deque.buckets[0][0]).toEqual(undefined); + expect(deque.buckets.length).toEqual(1); + deque.push(1); + deque.shrinkToFit(); + + deque = new Deque([1, 2, 3, 4, 5], { bucketSize: 2 }); + expect(deque.size).toBe(5); + expect(deque.has(1)).toBe(true); + expect(deque.bucketFirst).toBe(0); + expect(deque.bucketLast).toBe(2); + expect(deque.firstInBucket).toBe(0); + expect(deque.lastInBucket).toBe(0); + expect(deque.bucketCount).toBe(3); + expect(deque.buckets[0][0]).toBe(1); + expect(deque.buckets[2][0]).toBe(5); + expect(deque.buckets.length).toBe(3); + deque.shrinkToFit(); + expect(deque.buckets).toEqual([[1, 2], [3, 4], [5]]); + deque.push(6); + deque.push(7); + deque.shrinkToFit(); + expect(deque.buckets).toEqual([ + [1, 2], + [3, 4], + [5, 6], + [7, 2] + ]); + }); }); describe('Deque - Utility Operations', () => { let deque: Deque; @@ -213,7 +271,8 @@ describe('Deque - Utility Operations', () => { deque = new Deque(); }); - test('find should return the first element that matches the condition', () => { + it('find should return the first element that matches the condition', () => { + expect(deque.first).toBe(undefined); deque.push(1); deque.push(2); deque.push(3); @@ -221,22 +280,23 @@ describe('Deque - Utility Operations', () => { expect(found).toBe(2); }); - test('indexOf should return the index of the first occurrence of an element', () => { + it('indexOf should return the index of the first occurrence of an element', () => { deque.push(1); deque.push(2); deque.push(3); const index = deque.indexOf(2); expect(index).toBe(1); + expect(deque.indexOf(4)).toBe(-1); }); - test('toArray should convert the deque to an array', () => { + it('toArray should convert the deque to an array', () => { deque.push(1); deque.push(2); deque.push(3); expect(deque.toArray()).toEqual([1, 2, 3]); }); - test('filter should filter elements based on a predicate', () => { + it('filter should filter elements based on a predicate', () => { deque.push(1); deque.push(2); deque.push(3); @@ -244,7 +304,7 @@ describe('Deque - Utility Operations', () => { expect(filtered.toArray()).toEqual([2, 3]); }); - test('map should apply a function to all elements', () => { + it('map should apply a function to all elements', () => { deque.push(1); deque.push(2); deque.push(3); @@ -252,7 +312,7 @@ describe('Deque - Utility Operations', () => { expect(mapped.toArray()).toEqual([2, 4, 6]); }); - test('print should print the deque elements', () => { + it('print should print the deque elements', () => { // const consoleSpy = jest.spyOn(console, 'log'); // deque.push(1); // deque.push(2); @@ -260,7 +320,7 @@ describe('Deque - Utility Operations', () => { // expect(consoleSpy).toHaveBeenCalledWith([1, 2]); }); - test('should maxLen work well', () => { + it('should maxLen work well', () => { const dequeMaxLen = new Deque([3, 4, 5, 6, 7], { maxLen: 3 }); expect(dequeMaxLen.size).toBe(3); expect(dequeMaxLen.toArray()).toEqual([5, 6, 7]); @@ -286,42 +346,43 @@ describe('Deque - Additional Operations', () => { deque = new Deque(); }); - test('push should add an element to the end', () => { + it('push should add an element to the end', () => { deque.push(1); deque.push(2); expect(deque.last).toBe(2); expect(deque.size).toBe(2); }); - test('pop should remove and return the last element', () => { + it('pop should remove and return the last element', () => { deque.push(1); deque.push(2); expect(deque.pop()).toBe(2); expect(deque.size).toBe(1); }); - test('unshift should add an element to the beginning', () => { + it('unshift should add an element to the beginning', () => { deque.unshift(1); deque.unshift(2); expect(deque.first).toBe(2); expect(deque.size).toBe(2); }); - test('shift should remove and return the first element', () => { + it('shift should remove and return the first element', () => { + deque.shift(); deque.unshift(1); deque.unshift(2); expect(deque.shift()).toBe(2); expect(deque.size).toBe(1); }); - test('clear should reset the deque', () => { + it('clear should reset the deque', () => { deque.unshift(1); deque.clear(); expect(deque.size).toBe(0); expect(deque.isEmpty()).toBeTruthy(); }); - test('begin should yield elements from the beginning', () => { + it('begin should yield elements from the beginning', () => { deque.push(1); deque.push(2); const iterator = deque.begin(); @@ -329,7 +390,7 @@ describe('Deque - Additional Operations', () => { expect(iterator.next().value).toBe(2); }); - test('reverseBegin should yield elements in reverse order', () => { + it('reverseBegin should yield elements in reverse order', () => { deque.push(1); deque.push(2); const iterator = deque.reverseBegin(); @@ -347,13 +408,13 @@ describe('Deque - push Method', () => { }); }); - test('push should add an element when deque is empty', () => { + it('push should add an element when deque is empty', () => { deque.push(1); expect(deque.last).toBe(1); expect(deque.size).toBe(1); }); - test('push should add an element when lastInBucket is not at max', () => { + it('push should add an element when lastInBucket is not at max', () => { for (let i = 0; i < bucketSize - 1; i++) { deque.push(i); } @@ -362,7 +423,7 @@ describe('Deque - push Method', () => { expect(deque.size).toBe(bucketSize); }); - test('push should add an element and move to next bucket when last bucket is full', () => { + it('push should add an element and move to next bucket when last bucket is full', () => { for (let i = 0; i < bucketSize; i++) { deque.push(i); } @@ -371,7 +432,7 @@ describe('Deque - push Method', () => { expect(deque.size).toBe(bucketSize + 1); }); - test('push should add an element and reallocate when last bucket and lastInBucket are at max', () => { + it('push should add an element and reallocate when last bucket and lastInBucket are at max', () => { for (let i = 0; i < 100; i++) { deque.push(i); } @@ -391,20 +452,20 @@ describe('Deque - pop Method', () => { }); }); - test('pop should remove and return the last element', () => { + it('pop should remove and return the last element', () => { deque.push(1); deque.push(2); expect(deque.pop()).toBe(2); expect(deque.size).toBe(1); }); - test('pop should handle popping the only element', () => { + it('pop should handle popping the only element', () => { deque.push(1); expect(deque.pop()).toBe(1); expect(deque.isEmpty()).toBeTruthy(); }); - test('pop should adjust bucketLast and lastInBucket correctly', () => { + it('pop should adjust bucketLast and lastInBucket correctly', () => { for (let i = 0; i < 100; i++) { deque.push(i); } @@ -424,13 +485,13 @@ describe('Deque - unshift Method', () => { }); }); - test('unshift should add an element to the beginning when deque is empty', () => { + it('unshift should add an element to the beginning when deque is empty', () => { deque.unshift(1); expect(deque.first).toBe(1); expect(deque.size).toBe(1); }); - test('unshift should add an element to the beginning and adjust firstInBucket', () => { + it('unshift should add an element to the beginning and adjust firstInBucket', () => { for (let i = 0; i < 100; i++) { deque.unshift(i); } @@ -439,7 +500,7 @@ describe('Deque - unshift Method', () => { expect(deque.first).toBe(0); }); - test('unshift should add an element and reallocate when needed', () => { + it('unshift should add an element and reallocate when needed', () => { for (let i = 0; i < 100; i++) { deque.unshift(i); } @@ -458,20 +519,20 @@ describe('Deque - shift Method', () => { }); }); - test('shift should remove and return the first element', () => { + it('shift should remove and return the first element', () => { deque.push(1); deque.push(2); expect(deque.shift()).toBe(1); expect(deque.size).toBe(1); }); - test('shift should handle shifting the only element', () => { + it('shift should handle shifting the only element', () => { deque.push(1); expect(deque.shift()).toBe(1); expect(deque.isEmpty()).toBeTruthy(); }); - test('shift should adjust bucketFirst and firstInBucket correctly', () => { + it('shift should adjust bucketFirst and firstInBucket correctly', () => { for (let i = 0; i < 100; i++) { deque.push(i); } @@ -481,3 +542,183 @@ describe('Deque - shift Method', () => { } }); }); + +describe('Deque', () => { + it('should initialize with default iterable with length function', () => { + class IterableNumbers { + private readonly _elements: number[] = []; + + constructor(elements: number[]) { + this._elements = elements; + } + + *[Symbol.iterator]() { + for (let i = 0; i < this._elements.length; ++i) { + yield this._elements[i]; + } + } + + length() { + return this._elements.length; + } + } + + const numbers = new IterableNumbers([1, 6, 7, 3, 2, 4, 5]); + const deque = new Deque(numbers, { bucketSize: 3 }); + expect(deque.size).toBe(7); + expect(deque.bucketSize).toBe(3); + expect(deque.maxLen).toBe(-1); + }); + + it('should initialize with default iterable with size function', () => { + class IterableNumbersWithSize { + private readonly _elements: number[] = []; + + constructor(elements: number[]) { + this._elements = elements; + } + + *[Symbol.iterator]() { + for (let i = 0; i < this._elements.length; ++i) { + yield this._elements[i]; + } + } + + size() { + return this._elements.length; + } + } + + const numbers = new IterableNumbersWithSize([1, 6, 7, 3, 2, 4, 5]); + const deque = new Deque(numbers, { bucketSize: 3 }); + expect(deque.size).toBe(7); + expect(deque.bucketSize).toBe(3); + expect(deque.maxLen).toBe(-1); + }); + + it('should initialize via toElementFn', () => { + const objArr: Array<{ + key: number; + }> = [{ key: 1 }, { key: 6 }, { key: 7 }, { key: 3 }, { key: 2 }, { key: 4 }, { key: 5 }]; + const deque = new Deque(objArr, { toElementFn: item => item.key }); + expect(deque.size).toBe(7); + expect(deque.has(1)).toBe(true); + expect(deque.has(7)).toBe(true); + expect(deque.has(8)).toBe(false); + }); + + it('should bucket properties are correct', () => { + const objArr: Array<{ + key: number; + }> = [{ key: 1 }, { key: 6 }, { key: 7 }, { key: 3 }, { key: 2 }, { key: 4 }, { key: 5 }]; + const deque = new Deque(objArr, { toElementFn: item => item.key, bucketSize: 3 }); + expect(deque.size).toBe(7); + expect(deque.has(1)).toBe(true); + expect(deque.bucketFirst).toBe(0); + expect(deque.bucketLast).toBe(2); + expect(deque.firstInBucket).toBe(1); + expect(deque.lastInBucket).toBe(1); // TODO may be a problem + expect(deque.bucketCount).toBe(3); + expect(deque.buckets).toEqual([ + [, 1, 6], + [7, 3, 2], + [4, 5] + ]); + }); + + it('should pop work well when bucket boundary is reached', () => { + const deque = new Deque([1, 6, 7, 3, 2, 4, 5], { bucketSize: 3 }); + expect(deque.size).toBe(7); + expect(deque.has(1)).toBe(true); + expect(deque.bucketFirst).toBe(0); + expect(deque.bucketLast).toBe(2); + expect(deque.firstInBucket).toBe(1); + expect(deque.lastInBucket).toBe(1); // TODO may be a problem + expect(deque.bucketCount).toBe(3); + expect(deque.buckets).toEqual([ + [, 1, 6], + [7, 3, 2], + [4, 5] + ]); + for (let i = 0; i < 3; ++i) deque.pop(); + expect(deque.size).toBe(4); + expect(deque.has(1)).toBe(true); + expect(deque.bucketFirst).toBe(0); + expect(deque.bucketLast).toBe(1); + expect(deque.firstInBucket).toBe(1); + expect(deque.lastInBucket).toBe(1); + expect(deque.bucketCount).toBe(3); + expect(deque.buckets).toEqual([ + [, 1, 6], + [7, 3, 2], + [4, 5] + ]); // TODO may be a problem + deque.pop(); + expect(deque.size).toBe(3); + expect(deque.has(1)).toBe(true); + expect(deque.bucketFirst).toBe(0); + expect(deque.bucketLast).toBe(1); + expect(deque.firstInBucket).toBe(1); + expect(deque.lastInBucket).toBe(0); + expect(deque.bucketCount).toBe(3); + expect(deque.buckets).toEqual([ + [, 1, 6], + [7, 3, 2], + [4, 5] + ]); // TODO may be a problem + }); + + it('should shift work well when bucket boundary is reached and should shrinkToFit', () => { + const deque = new Deque([1, 6, 7, 3, 2, 4, 5], { bucketSize: 3 }); + expect(deque.size).toBe(7); + expect(deque.has(1)).toBe(true); + expect(deque.bucketFirst).toBe(0); + expect(deque.bucketLast).toBe(2); + expect(deque.firstInBucket).toBe(1); + expect(deque.lastInBucket).toBe(1); // TODO may be a problem + expect(deque.bucketCount).toBe(3); + expect(deque.buckets).toEqual([ + [, 1, 6], + [7, 3, 2], + [4, 5] + ]); + for (let i = 0; i < 3; ++i) deque.shift(); + expect(deque.size).toBe(4); + expect(deque.has(1)).toBe(false); + expect(deque.bucketFirst).toBe(1); + expect(deque.bucketLast).toBe(2); + expect(deque.firstInBucket).toBe(1); + expect(deque.lastInBucket).toBe(1); + expect(deque.bucketCount).toBe(3); + expect(deque.buckets).toEqual([ + [, 1, 6], + [7, 3, 2], + [4, 5] + ]); // TODO may be a problem + deque.shift(); + expect(deque.size).toBe(3); + expect(deque.has(1)).toBe(false); + expect(deque.bucketFirst).toBe(1); + expect(deque.bucketLast).toBe(2); + expect(deque.firstInBucket).toBe(2); + expect(deque.lastInBucket).toBe(1); + expect(deque.bucketCount).toBe(3); + expect(deque.buckets).toEqual([ + [, 1, 6], + [7, 3, 2], + [4, 5] + ]); // TODO may be a problem + deque.shrinkToFit(); + expect(deque.size).toBe(3); + expect(deque.has(1)).toBe(false); + expect(deque.bucketFirst).toBe(0); + expect(deque.bucketLast).toBe(1); + expect(deque.firstInBucket).toBe(2); + expect(deque.lastInBucket).toBe(1); + expect(deque.bucketCount).toBe(3); + expect(deque.buckets).toEqual([ + [7, 3, 2], + [4, 5] + ]); // TODO may be a problem + }); +}); diff --git a/test/unit/data-structures/queue/queue.test.ts b/test/unit/data-structures/queue/queue.test.ts index c6cdee8..6dd4ef5 100644 --- a/test/unit/data-structures/queue/queue.test.ts +++ b/test/unit/data-structures/queue/queue.test.ts @@ -10,68 +10,68 @@ describe('Queue', () => { queue = new Queue(); }); - test('new Queue() should create an empty queue', () => { + it('new Queue() should create an empty queue', () => { expect(queue.size).toBe(0); expect(queue.isEmpty()).toBeTruthy(); }); - test('push should add elements to the queue', () => { + it('push should add elements to the queue', () => { queue.push(1); queue.push(2); expect(queue.size).toBe(2); }); - test('shift should remove the first element', () => { + it('shift should remove the first element', () => { queue.push(1); queue.push(2); expect(queue.shift()).toBe(1); expect(queue.size).toBe(1); }); - test('shift should return undefined if queue is empty', () => { + it('shift should return undefined if queue is empty', () => { expect(queue.shift()).toBeUndefined(); }); - test('first should return the first element without removing it', () => { + it('first should return the first element without removing it', () => { queue.push(1); queue.push(2); expect(queue.first).toBe(1); expect(queue.size).toBe(2); }); - test('first should return undefined if queue is empty', () => { + it('first should return undefined if queue is empty', () => { expect(queue.first).toBeUndefined(); }); - test('size should return the number of elements', () => { + it('size should return the number of elements', () => { queue.push(1); queue.push(2); expect(queue.size).toBe(2); }); - test('isEmpty should return true if the queue is empty', () => { + it('isEmpty should return true if the queue is empty', () => { expect(queue.isEmpty()).toBeTruthy(); }); - test('isEmpty should return false if the queue is not empty', () => { + it('isEmpty should return false if the queue is not empty', () => { queue.push(1); expect(queue.isEmpty()).toBeFalsy(); }); - test('toArray should return an array of queue elements', () => { + it('toArray should return an array of queue elements', () => { queue.push(1); queue.push(2); expect(queue.toArray()).toEqual([1, 2]); }); - test('clear should remove all elements from the queue', () => { + it('clear should remove all elements from the queue', () => { queue.push(1); queue.push(2); queue.clear(); expect(queue.size).toBe(0); }); - test('forEach should iterate over all elements', () => { + it('forEach should iterate over all elements', () => { const arr: number[] = []; queue.push(1); queue.push(2); @@ -80,7 +80,7 @@ describe('Queue', () => { }); // Boundary value testing - test('push and shift with many elements', () => { + it('push and shift with many elements', () => { for (let i = 0; i < 1000; i++) { queue.push(i); } @@ -90,7 +90,7 @@ describe('Queue', () => { expect(queue.isEmpty()).toBeTruthy(); }); - test('compact method should work well', () => { + it('compact method should work well', () => { for (let i = 0; i < 1000; i++) queue.push(i); for (let i = 0; i < 499; i++) queue.shift(); @@ -100,7 +100,7 @@ describe('Queue', () => { expect(queue.elements.length).toBe(501); }); - test('should at after shifting', () => { + it('should at after shifting', () => { for (let i = 0; i < 100; i++) { queue.push(i); } @@ -114,7 +114,7 @@ describe('Queue', () => { } }); - test('should toElementFn', () => { + it('should toElementFn', () => { const queue = new Queue([{ id: '1' }, { id: '5' }, { id: '3' }, { id: '4' }, { id: '2' }], { toElementFn: rawElement => rawElement.id }); @@ -177,6 +177,29 @@ describe('Queue', () => { expect([...queue]).toEqual(['1', '6', '9']); expect([...cloned]).toEqual(['1', '6', '0', '5', '9']); }); + + it('should set autoCompactRatio', function () { + const queue = new Queue(); + + queue.autoCompactRatio = 0.3; + queue.push(1); + queue.push(2); + queue.push(3); + queue.push(4); + queue.push(5); + queue.push(6); + queue.push(7); + queue.push(8); + queue.push(9); + queue.push(10); + expect(queue.elements.length).toBe(10); + while (queue.size > 7) queue.shift(); + expect(queue.size).toBe(7); + expect(queue.elements.length).toBe(10); + queue.shift(); + expect(queue.size).toBe(6); + expect(queue.elements.length).toBe(6); + }); }); describe('Queue - Advanced Methods', () => { @@ -186,7 +209,7 @@ describe('Queue - Advanced Methods', () => { queue = new Queue(); }); - test('reduce should apply a function against an accumulator and each element', () => { + it('reduce should apply a function against an accumulator and each element', () => { queue.push(1); queue.push(2); queue.push(3); @@ -194,13 +217,13 @@ describe('Queue - Advanced Methods', () => { expect(sum).toBe(6); }); - test('reduce should return initial value for empty queue', () => { + it('reduce should return initial value for empty queue', () => { const initialValue = 0; const sum = queue.reduce((acc, val) => acc + val, initialValue); expect(sum).toBe(initialValue); }); - test('filter should return a new queue with all elements that pass the test implemented by provided function', () => { + it('filter should return a new queue with all elements that pass the test implemented by provided function', () => { queue.push(1); queue.push(2); queue.push(3); @@ -208,12 +231,12 @@ describe('Queue - Advanced Methods', () => { expect(filteredQueue.toArray()).toEqual([2, 3]); }); - test('filter should return an empty queue for empty queue', () => { + it('filter should return an empty queue for empty queue', () => { const filteredQueue = queue.filter(val => val > 1); expect(filteredQueue.isEmpty()).toBeTruthy(); }); - test('map should create a new queue with the results of calling a provided function on every element', () => { + it('map should create a new queue with the results of calling a provided function on every element', () => { queue.push(1); queue.push(2); queue.push(3); @@ -221,7 +244,7 @@ describe('Queue - Advanced Methods', () => { expect(mappedQueue.toArray()).toEqual([2, 4, 6]); }); - test('map should return an empty queue for empty queue', () => { + it('map should return an empty queue for empty queue', () => { const mappedQueue = queue.map(val => val * 2); expect(mappedQueue.isEmpty()).toBeTruthy(); }); @@ -233,31 +256,31 @@ describe('Queue - Additional Methods', () => { queue = new Queue(); }); - test('peekLast should return the last element without removing it', () => { + it('peekLast should return the last element without removing it', () => { queue.push(1); queue.push(2); expect(queue.last).toBe(2); expect(queue.size).toBe(2); }); - test('peekLast should return undefined if queue is empty', () => { + it('peekLast should return undefined if queue is empty', () => { expect(queue.last).toBeUndefined(); }); - test('at should return the element at the specified index', () => { + it('at should return the element at the specified index', () => { queue.push(1); queue.push(2); queue.push(3); expect(queue.at(1)).toBe(2); }); - test('at should return undefined for an invalid index', () => { + it('at should return undefined for an invalid index', () => { queue.push(1); expect(queue.at(3)).toBeUndefined(); expect(queue.at(-1)).toBeUndefined(); }); - test('print should not throw any errors', () => { + it('print should not throw any errors', () => { expect(() => { queue.push(1); // queue.print(); @@ -266,19 +289,19 @@ describe('Queue - Additional Methods', () => { }); describe('Queue - Static and Clone Methods', () => { - test('fromArray should create a new queue from an array', () => { + it('fromArray should create a new queue from an array', () => { const array = [1, 2, 3]; const queue = Queue.fromArray(array); expect(queue.toArray()).toEqual(array); expect(queue.size).toBe(array.length); }); - test('fromArray should create an empty queue from an empty array', () => { + it('fromArray should create an empty queue from an empty array', () => { const queue = Queue.fromArray([]); expect(queue.isEmpty()).toBeTruthy(); }); - test('clone should create a new queue with the same elements', () => { + it('clone should create a new queue with the same elements', () => { const originalQueue = new Queue(); originalQueue.push(1); originalQueue.push(2); @@ -288,7 +311,7 @@ describe('Queue - Static and Clone Methods', () => { expect(clonedQueue.size).toBe(originalQueue.size); }); - test('clone should not affect the original queue when mutated', () => { + it('clone should not affect the original queue when mutated', () => { const originalQueue = new Queue(); originalQueue.push(1); originalQueue.push(2); @@ -306,18 +329,16 @@ describe('LinkedListQueue', () => { beforeEach(() => { queue = new LinkedListQueue(); + queue.push('A'); + queue.push('B'); }); it('should push elements to the end of the queue', () => { - queue.push('A'); - queue.push('B'); expect(queue.first).toBe('A'); expect(queue.size).toBe(2); }); it('should shift elements from the front of the queue', () => { - queue.push('A'); - queue.push('B'); const dequeued = queue.shift(); expect(dequeued).toBe('A'); expect(queue.first).toBe('B'); @@ -325,8 +346,12 @@ describe('LinkedListQueue', () => { }); it('should peek at the front of the queue', () => { - queue.push('A'); - queue.push('B'); expect(queue.first).toBe('A'); }); + + it('should clone method work correctly', () => { + const cloned = queue.clone(); + expect(cloned instanceof LinkedListQueue).toBe(true); + expect(cloned.size).toBe(2); + }); }); diff --git a/test/unit/data-structures/stack/stack.test.ts b/test/unit/data-structures/stack/stack.test.ts index 4cc1725..ede31ac 100644 --- a/test/unit/data-structures/stack/stack.test.ts +++ b/test/unit/data-structures/stack/stack.test.ts @@ -19,6 +19,7 @@ describe('Stack', () => { }); it('should peek at the top element without removing it', () => { + expect(stack.peek()).toBe(undefined); stack.push(1); stack.push(2); stack.push(3); @@ -93,7 +94,7 @@ describe('Stack iterative methods', () => { stack.push(3); }); - test('should iterate through the stack', () => { + it('should iterate through the stack', () => { const result: number[] = []; for (const element of stack) { result.push(element); @@ -102,7 +103,7 @@ describe('Stack iterative methods', () => { expect(result).toEqual([1, 2, 3]); // iteration should start from the top of the stack }); - test('should apply forEach to the stack', () => { + it('should apply forEach to the stack', () => { const result: number[] = []; stack.forEach(element => { result.push(element); @@ -111,23 +112,36 @@ describe('Stack iterative methods', () => { expect(result).toEqual([1, 2, 3]); }); - test('should filter elements in the stack', () => { + it('should filter elements in the stack', () => { const filteredStack = stack.filter(element => element > 1); expect(filteredStack.size).toBe(2); expect([...filteredStack]).toEqual([2, 3]); }); - test('should map elements in the stack', () => { + it('should map elements in the stack', () => { const mappedStack = stack.map(element => element * 2); expect(mappedStack.size).toBe(3); expect([...mappedStack]).toEqual([2, 4, 6]); }); - test('should reduce elements in the stack', () => { + it('should reduce elements in the stack', () => { const sum = stack.reduce((accumulator, element) => accumulator + element, 0); expect(sum).toBe(6); }); + + it('should toElementFn', () => { + const stack = new Stack([{ key: 1 }, { key: 2 }, { key: 5 }, { key: 3 }], { toElementFn: item => item.key }); + expect(stack.size).toBe(4); + expect([...stack]).toEqual([1, 2, 5, 3]); + }); + + it('should fromArray', () => { + const stack = Stack.fromArray([1, 2, 5, 3]); + expect(stack instanceof Stack).toBe(true); + expect(stack.size).toBe(4); + expect([...stack]).toEqual([1, 2, 5, 3]); + }); }); diff --git a/test/unit/data-structures/tree/tree.test.ts b/test/unit/data-structures/tree/tree.test.ts new file mode 100644 index 0000000..4c7e16b --- /dev/null +++ b/test/unit/data-structures/tree/tree.test.ts @@ -0,0 +1,58 @@ +import { TreeNode } from '../../../../src'; + +describe('TreeNode', () => { + it('should create a tree node with key and value', () => { + const node = new TreeNode('root', 'RootValue'); + expect(node.key).toBe('root'); + expect(node.value).toBe('RootValue'); + expect(node.children).toEqual(undefined); + }); + + it('should allow setting and getting key and value', () => { + const node = new TreeNode('node1', 'Value1'); + node.key = 'newKey'; + node.value = 'newValue'; + expect(node.key).toBe('newKey'); + expect(node.value).toBe('newValue'); + }); + + it('should add a single child node', () => { + const parent = new TreeNode('parent'); + const child = new TreeNode('child', 'ChildValue'); + + parent.addChildren(child); + + expect(parent.children).toHaveLength(1); + expect(parent.children?.[0].key).toBe('child'); + expect(parent.children?.[0].value).toBe('ChildValue'); + }); + + it('should add multiple children nodes', () => { + const parent = new TreeNode('parent'); + const child1 = new TreeNode('child1'); + const child2 = new TreeNode('child2'); + + parent.addChildren([child1, child2]); + + expect(parent.children).toHaveLength(2); + expect(parent.children?.[0].key).toBe('child1'); + expect(parent.children?.[1].key).toBe('child2'); + parent.children = []; + + expect(parent.children[0]).toBe(undefined); + expect(parent.children[1]).toBe(undefined); + }); + + it('should calculate the correct height of the tree', () => { + const root = new TreeNode('root'); + const child1 = new TreeNode('child1'); + const child2 = new TreeNode('child2'); + const grandChild = new TreeNode('grandChild'); + + root.addChildren(child1); + root.addChildren(child2); + child1.addChildren(grandChild); + + expect(root.getHeight()).toBe(2); // root -> child1 -> grandChild (height = 2) + }); +}); diff --git a/test/unit/data-structures/trie/trie.test.ts b/test/unit/data-structures/trie/trie.test.ts index 4b87714..ea93a5f 100644 --- a/test/unit/data-structures/trie/trie.test.ts +++ b/test/unit/data-structures/trie/trie.test.ts @@ -22,6 +22,31 @@ describe('TrieNode', () => { node.isEnd = true; expect(node.isEnd).toBe(true); }); + + it('should set key property correctly', () => { + const node = new TrieNode('a'); + node.isEnd = false; + expect(node).toEqual({ + _children: new Map(), + _isEnd: false, + _key: 'a' + }); + node.key = 'b'; + expect(node.key).toBe('b'); + expect(node).toEqual({ + _children: new Map(), + _isEnd: false, + _key: 'b' + }); + }); + + it('should set children property correctly', () => { + const node = new TrieNode('a'); + node.isEnd = false; + const children = new Map([['p', new TrieNode('p')]]); + node.children = children; + expect(node.children).toEqual(children); + }); }); describe('Trie', () => { @@ -865,29 +890,45 @@ describe('Trie class', () => { trie = new Trie(['apple', 'app', 'banana', 'band', 'bandana']); }); - test('[Symbol.iterator] should iterate over all words', () => { + it('[Symbol.iterator] should iterate over all words', () => { const words = [...trie]; expect(words).toEqual(['app', 'apple', 'banana', 'band', 'bandana']); }); - test('forEach should execute a callback for each word', () => { + it('forEach should execute a callback for each word', () => { const mockCallback = jest.fn(); trie.forEach(mockCallback); expect(mockCallback).toHaveBeenCalledTimes(5); }); - test('filter should return words that satisfy the predicate', () => { + it('filter should return words that satisfy the predicate', () => { const filteredWords = trie.filter(word => word.startsWith('ba')); expect([...filteredWords]).toEqual(['banana', 'band', 'bandana']); }); - test('map should apply a function to each word', () => { + it('map should apply a function to each word', () => { const mappedWords = trie.map(word => word.length.toString()); expect([...mappedWords]).toEqual(['3', '5', '6', '4', '7']); }); - test('reduce should reduce the words to a single value', () => { + it('reduce should reduce the words to a single value', () => { const concatenatedWords = trie.reduce((acc, word) => acc + word, ''); expect(concatenatedWords).toEqual('appapplebananabandbandana'); }); + + it('reduce should new Trie with toElementFn be correct', () => { + const trieB = new Trie([{ name: 'apple' }, { name: 'app' }, { name: 'arm' }], { toElementFn: item => item.name }); + expect(trieB.isEmpty()).toBe(false); + expect(trieB.size).toBe(3); + expect(trieB.has('apple')).toBe(true); + expect(trieB.has('app')).toBe(true); + expect(trieB.has('arm')).toBe(true); + expect(trieB.hasPrefix('ap')).toBe(true); + trieB.clear(); + expect(trieB.size).toBe(0); + expect(trieB.has('apple')).toBe(false); + expect(trieB.has('app')).toBe(false); + expect(trieB.has('arm')).toBe(false); + expect(trieB.hasPrefix('ap')).toBe(false); + }); });