From b4e89d9f72408ba41df3caa7d3f00ed4350bad8d Mon Sep 17 00:00:00 2001 From: Revone Date: Sun, 19 Nov 2023 21:09:44 +0800 Subject: [PATCH] refactor: Discard the unnecessary Iterator class in Deque and HashMap, and instead implement using Generators. --- README.md | 47 +--- src/data-structures/hash/hash-map.ts | 221 ++---------------- src/data-structures/queue/deque.ts | 29 +-- .../data-structures/hash/hash-map.test.ts | 9 +- test/unit/data-structures/queue/deque.test.ts | 25 -- 5 files changed, 40 insertions(+), 291 deletions(-) diff --git a/README.md b/README.md index e680504..d64848f 100644 --- a/README.md +++ b/README.md @@ -807,53 +807,8 @@ Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.key) // ['A', 'B', ' [//]: # (No deletion!!! Start of Replace Section)
-
comparison
-
test nametime taken (ms)executions per secsample deviation
SRC 10,000 add0.571745.234.83e-6
CJS 10,000 add0.571752.474.64e-6
MJS 10,000 add0.591687.891.40e-4
SRC PQ 10,000 add & pop3.41293.002.65e-5
CJS PQ 10,000 add & pop4.92203.313.60e-5
MJS PQ 10,000 add & pop4.88204.724.35e-5
-
-
avl-tree
-
test nametime taken (ms)executions per secsample deviation
10,000 add randomly31.0232.242.83e-4
10,000 add & delete randomly71.4514.000.00
10,000 addMany40.2124.874.47e-4
10,000 get28.3435.295.15e-4
-
-
binary-tree
-
test nametime taken (ms)executions per secsample deviation
1,000 add randomly13.0476.711.56e-4
1,000 add & delete randomly15.7963.331.40e-4
1,000 addMany10.2597.601.08e-4
1,000 get18.3154.601.50e-4
1,000 dfs155.226.448.11e-4
1,000 bfs56.6617.656.70e-4
1,000 morris254.793.925.27e-4
-
-
bst
-
test nametime taken (ms)executions per secsample deviation
10,000 add randomly28.2335.423.20e-4
10,000 add & delete randomly68.3514.638.46e-4
10,000 addMany28.9434.560.00
10,000 get28.9734.514.57e-4
-
-
rb-tree
-
test nametime taken (ms)executions per secsample deviation
100,000 add89.1211.220.00
100,000 add & delete randomly219.004.570.00
100,000 getNode105.749.466.09e-4
-
-
directed-graph
-
test nametime taken (ms)executions per secsample deviation
1,000 addVertex0.109902.149.12e-7
1,000 addEdge6.11163.671.29e-4
1,000 getVertex0.052.16e+43.51e-7
1,000 getEdge23.6342.330.00
tarjan222.854.490.01
tarjan all223.794.470.00
topologicalSort181.775.500.00
-
-
hash-map
-
test nametime taken (ms)executions per secsample deviation
10,000 set1.001001.932.06e-5
10,000 set & get1.51661.532.65e-5
-
-
heap
-
test nametime taken (ms)executions per secsample deviation
10,000 add & pop5.82171.795.13e-5
10,000 fib add & pop359.242.780.00
-
-
doubly-linked-list
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push214.504.660.05
1,000,000 unshift205.894.860.05
1,000,000 unshift & shift168.935.920.04
1,000,000 insertBefore291.323.430.05
-
-
singly-linked-list
-
test nametime taken (ms)executions per secsample deviation
10,000 push & pop215.824.630.02
10,000 insertBefore248.564.020.01
-
-
max-priority-queue
-
test nametime taken (ms)executions per secsample deviation
10,000 refill & poll8.85113.011.90e-4
-
-
priority-queue
-
test nametime taken (ms)executions per secsample deviation
100,000 add & pop104.239.590.00
-
deque
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push14.2570.181.63e-4
1,000,000 push & pop23.1543.212.07e-4
1,000,000 push & shift24.1641.401.78e-4
1,000,000 unshift & shift22.2844.881.56e-4
-
-
queue
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push45.7221.870.01
1,000,000 push & shift81.7512.230.00
-
-
stack
-
test nametime taken (ms)executions per secsample deviation
1,000,000 push44.3022.570.01
1,000,000 push & pop49.6920.120.01
-
-
trie
-
test nametime taken (ms)executions per secsample deviation
100,000 push43.1023.206.46e-4
100,000 getWords85.4511.700.00
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push13.4974.161.40e-4
1,000,000 push & pop22.4844.480.00
1,000,000 push & shift23.4242.711.59e-4
1,000,000 unshift & shift21.6246.252.77e-4
[//]: # (No deletion!!! End of Replace Section) diff --git a/src/data-structures/hash/hash-map.ts b/src/data-structures/hash/hash-map.ts index deb41be..82bc63d 100644 --- a/src/data-structures/hash/hash-map.ts +++ b/src/data-structures/hash/hash-map.ts @@ -6,127 +6,8 @@ * @license MIT License */ -import { isObjOrFunc, rangeCheck, throwRangeError } from '../../utils'; -import { HashMapLinkedNode, IterableWithSizeOrLength, IterateDirection } from '../../types'; - -/** - * Because the implementation of HashMap relies on JavaScript's built-in objects and arrays, - * these underlying structures have already dealt with dynamic expansion and hash collisions. - * Therefore, there is no need for additional logic to handle these issues. - */ -export class HashMapIterator { - readonly hashMap: HashMap; - readonly iterateDirection: IterateDirection; - protected _node: HashMapLinkedNode; - protected readonly _sentinel: HashMapLinkedNode; - - /** - * This is a constructor function for a linked list iterator in a HashMap data structure. - * @param node - The `node` parameter is a reference to a `HashMapLinkedNode` object. This object - * represents a node in a linked list used in a hash map data structure. It contains a key-value pair - * and references to the previous and next nodes in the linked list. - * @param sentinel - The `sentinel` parameter is a reference to a special node in a linked list. It - * is used to mark the beginning or end of the list and is typically used in data structures like - * hash maps or linked lists to simplify operations and boundary checks. - * @param hashMap - A HashMap object that stores key-value pairs. - * @param {IterateDirection} iterateDirection - The `iterateDirection` parameter is an optional - * parameter that specifies the direction in which the iterator should iterate over the elements of - * the HashMap. It can take one of the following values: - * @returns The constructor does not return anything. It is used to initialize the properties and - * methods of the object being created. - */ - constructor( - node: HashMapLinkedNode, - sentinel: HashMapLinkedNode, - hashMap: HashMap, - iterateDirection: IterateDirection = IterateDirection.DEFAULT - ) { - this._node = node; - this._sentinel = sentinel; - this.iterateDirection = iterateDirection; - - if (this.iterateDirection === IterateDirection.DEFAULT) { - this.prev = function () { - if (this._node.prev === this._sentinel) { - throwRangeError(); - } - this._node = this._node.prev; - return this; - }; - this.next = function () { - if (this._node === this._sentinel) { - throwRangeError(); - } - this._node = this._node.next; - return this; - }; - } else { - this.prev = function () { - if (this._node.next === this._sentinel) { - throwRangeError(); - } - this._node = this._node.next; - return this; - }; - this.next = function () { - if (this._node === this._sentinel) { - throwRangeError(); - } - this._node = this._node.prev; - return this; - }; - } - this.hashMap = hashMap; - } - - /** - * The above function returns a Proxy object that allows access to the key and value of a node in a - * data structure. - * @returns The code is returning a Proxy object. - */ - get current() { - if (this._node === this._sentinel) { - throwRangeError(); - } - - return new Proxy(<[K, V]>([]), { - get: (target, prop: '0' | '1') => { - if (prop === '0') return this._node.key; - else if (prop === '1') return this._node.value; - target[0] = this._node.key; - target[1] = this._node.value; - return target[prop]; - }, - set: (_, prop: '1', newValue: V) => { - if (prop !== '1') { - throw new TypeError(`prop should be string '1'`); - } - this._node.value = newValue; - return true; - } - }); - } - - /** - * The function checks if a node is accessible. - * @returns a boolean value indicating whether the `_node` is not equal to the `_sentinel`. - */ - isAccessible() { - return this._node !== this._sentinel; - } - - prev() { - return this; - } - - next() { - return this; - } - - clone() { - return new HashMapIterator(this._node, this._sentinel, this.hashMap, this.iterateDirection) - } -} +import { isObjOrFunc, rangeCheck } from '../../utils'; +import { HashMapLinkedNode, IterableWithSizeOrLength } from '../../types'; export class HashMap { readonly OBJ_KEY_INDEX = Symbol('OBJ_KEY_INDEX'); @@ -158,53 +39,6 @@ export class HashMap { return this._size; } - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The function returns a new iterator object for a HashMap. - * @returns A new instance of the HashMapIterator class is being returned. - */ - get begin() { - return new HashMapIterator(this._head, this._sentinel, this); - } - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The function returns a new HashMapIterator object with the _sentinel value as both the start and - * end values. - * @returns A new instance of the HashMapIterator class is being returned. - */ - get end() { - return new HashMapIterator(this._sentinel, this._sentinel, this); - } - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The reverseBegin function returns a new HashMapIterator object that iterates over the elements of - * a HashMap in reverse order. - * @returns A new instance of the HashMapIterator class is being returned. - */ - get reverseBegin() { - return new HashMapIterator(this._tail, this._sentinel, this, IterateDirection.REVERSE); - } - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The reverseEnd function returns a new HashMapIterator object that iterates over the elements of a - * HashMap in reverse order. - * @returns A new instance of the HashMapIterator class is being returned. - */ - get reverseEnd() { - return new HashMapIterator(this._sentinel, this._sentinel, this, IterateDirection.REVERSE); - } - /** * Time Complexity: O(1) * Space Complexity: O(1) @@ -231,6 +65,29 @@ export class HashMap { return <[K, V]>[this._tail.key, this._tail.value]; } + /** + * The `begin()` function in TypeScript iterates over a linked list and yields key-value pairs. + */ + * begin() { + let node = this._head; + while (node !== this._sentinel) { + yield [node.key, node.value]; + node = node.next; + } + } + + /** + * The function `reverseBegin()` iterates over a linked list in reverse order, yielding each node's + * key and value. + */ + * reverseBegin() { + let node = this._tail; + while (node !== this._sentinel) { + yield [node.key, node.value]; + node = node.prev; + } + } + /** * Time Complexity: O(1) * Space Complexity: O(1) @@ -332,34 +189,6 @@ export class HashMap { return <[K, V]>[node.key, node.value]; } - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The function `getIterator` returns a new instance of `HashMapIterator` based on the provided key - * and whether it is an object key or not. - * @param {K} key - The `key` parameter is the key used to retrieve the iterator from the HashMap. It - * can be of any type, depending on how the HashMap is implemented. - * @param {boolean} [isObjectKey] - The `isObjectKey` parameter is an optional boolean parameter that - * indicates whether the `key` parameter is an object key. If `isObjectKey` is `true`, it means that - * the `key` parameter is an object and needs to be handled differently. If `isObjectKey` is `false` - * @returns a new instance of the `HashMapIterator` class. - */ - getIterator(key: K, isObjectKey?: boolean) { - let node: HashMapLinkedNode; - if (isObjectKey) { - const index = (>(key))[this.OBJ_KEY_INDEX]; - if (index === undefined) { - node = this._sentinel; - } else { - node = this._nodes[index]; - } - } else { - node = this._orgMap[(key)] || this._sentinel; - } - return new HashMapIterator(node, this._sentinel, this); - } - /** * Time Complexity: O(1) * Space Complexity: O(1) diff --git a/src/data-structures/queue/deque.ts b/src/data-structures/queue/deque.ts index 6ec1bc4..b7cd79e 100644 --- a/src/data-structures/queue/deque.ts +++ b/src/data-structures/queue/deque.ts @@ -40,7 +40,7 @@ export class Deque { if ('length' in elements) { if (elements.length instanceof Function) _size = elements.length(); else _size = elements.length; } else { - if (elements.size instanceof Function) _size = elements.size();else _size = elements.size; + if (elements.size instanceof Function) _size = elements.size(); else _size = elements.size; } this._bucketSize = bucketSize; @@ -161,7 +161,10 @@ export class Deque { this._firstInBucket = this._lastInBucket = this._bucketSize >> 1; } - *begin(): Generator { + /** + * The below function is a generator that yields elements from a collection one by one. + */ + * begin(): Generator { let index = 0; while (index < this.size) { yield this.getAt(index); @@ -169,15 +172,11 @@ export class Deque { } } - *end(): Generator { - let index = this.size; - while (index > 0) { - index--; - yield this.getAt(index); - } - } - - *reverseBegin(): Generator { + /** + * The function `reverseBegin()` is a generator that yields elements in reverse order starting from + * the last element. + */ + * reverseBegin(): Generator { let index = this.size - 1; while (index >= 0) { yield this.getAt(index); @@ -185,14 +184,6 @@ export class Deque { } } - *reverseEnd(): Generator { - let index = -1; - while (index < this.size - 1) { - index++; - yield this.getAt(index); - } - } - /** * Time Complexity - Amortized O(1) (possible reallocation) * Space Complexity - O(n) (due to potential resizing). diff --git a/test/unit/data-structures/hash/hash-map.test.ts b/test/unit/data-structures/hash/hash-map.test.ts index 3688291..27708e0 100644 --- a/test/unit/data-structures/hash/hash-map.test.ts +++ b/test/unit/data-structures/hash/hash-map.test.ts @@ -172,15 +172,14 @@ describe('HashMap', () => { stdMap.forEach((value, key) => { if (index === 0) { expect(hashMap.first).toEqual([key, value]); - expect(hashMap.begin.current[0]).toEqual(key); + expect(hashMap.begin().next().value).toEqual([key, value]); } else if (index === hashMap.size - 1) { expect(hashMap.last).toEqual([key, value]); - expect(hashMap.reverseBegin.current[0]).toEqual(key); + expect(hashMap.reverseBegin().next().value).toEqual([key, value]); } else if (index <= 1000) { expect(hashMap.getAt(index)).toEqual([key, value]); } expect(hashMap.get(key)).toEqual(value); - expect(hashMap.getIterator(key).current[1]).toEqual(value); index++; }); } @@ -210,8 +209,8 @@ describe('HashMap', () => { test('should iterate correctly with reverse iterators', () => { hashMap.set('key1', 'value1'); hashMap.set('key2', 'value2'); - const iterator = hashMap.reverseBegin; - expect(iterator.next().current).toEqual(['key1', 'value1']); + const iterator = hashMap.reverseBegin(); + expect(iterator.next().value).toEqual(['key2', 'value2']); }); test('should return the last element', () => { diff --git a/test/unit/data-structures/queue/deque.test.ts b/test/unit/data-structures/queue/deque.test.ts index 4b5ba71..d5e23f3 100644 --- a/test/unit/data-structures/queue/deque.test.ts +++ b/test/unit/data-structures/queue/deque.test.ts @@ -434,19 +434,6 @@ describe('Deque', () => { }); }); - //Test end method - describe('end()', () => { - it('should return an iterator at the end of the deque', () => { - deque.push(1); - deque.push(2); - deque.push(3); - - const iterator = deque.end(); - - expect(iterator.next().value).toBe(3); - }); - }); - // Test the reverse Begin method describe('reverseBegin()', () => { it('should return a reverse iterator at the beginning of the deque', () => { @@ -460,16 +447,4 @@ describe('Deque', () => { }); }); - // Test the reverse End method - describe('reverseEnd()', () => { - it('should return a reverse iterator at the end of the deque', () => { - deque.push(1); - deque.push(2); - deque.push(3); - - const iterator = deque.reverseEnd(); - - expect(iterator.next().value).toBe(1); - }); - }); });