refactor: Discard the unnecessary Iterator class in Deque and HashMap, and instead implement using Generators.

This commit is contained in:
Revone 2023-11-19 21:09:44 +08:00
parent b089cf47b3
commit b4e89d9f72
5 changed files with 40 additions and 291 deletions

View file

@ -807,53 +807,8 @@ Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.key) // ['A', 'B', '
[//]: # (No deletion!!! Start of Replace Section)
<div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>comparison</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>SRC 10,000 add</td><td>0.57</td><td>1745.23</td><td>4.83e-6</td></tr><tr><td>CJS 10,000 add</td><td>0.57</td><td>1752.47</td><td>4.64e-6</td></tr><tr><td>MJS 10,000 add</td><td>0.59</td><td>1687.89</td><td>1.40e-4</td></tr><tr><td>SRC PQ 10,000 add & pop</td><td>3.41</td><td>293.00</td><td>2.65e-5</td></tr><tr><td>CJS PQ 10,000 add & pop</td><td>4.92</td><td>203.31</td><td>3.60e-5</td></tr><tr><td>MJS PQ 10,000 add & pop</td><td>4.88</td><td>204.72</td><td>4.35e-5</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>avl-tree</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 add randomly</td><td>31.02</td><td>32.24</td><td>2.83e-4</td></tr><tr><td>10,000 add & delete randomly</td><td>71.45</td><td>14.00</td><td>0.00</td></tr><tr><td>10,000 addMany</td><td>40.21</td><td>24.87</td><td>4.47e-4</td></tr><tr><td>10,000 get</td><td>28.34</td><td>35.29</td><td>5.15e-4</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>binary-tree</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000 add randomly</td><td>13.04</td><td>76.71</td><td>1.56e-4</td></tr><tr><td>1,000 add & delete randomly</td><td>15.79</td><td>63.33</td><td>1.40e-4</td></tr><tr><td>1,000 addMany</td><td>10.25</td><td>97.60</td><td>1.08e-4</td></tr><tr><td>1,000 get</td><td>18.31</td><td>54.60</td><td>1.50e-4</td></tr><tr><td>1,000 dfs</td><td>155.22</td><td>6.44</td><td>8.11e-4</td></tr><tr><td>1,000 bfs</td><td>56.66</td><td>17.65</td><td>6.70e-4</td></tr><tr><td>1,000 morris</td><td>254.79</td><td>3.92</td><td>5.27e-4</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>bst</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 add randomly</td><td>28.23</td><td>35.42</td><td>3.20e-4</td></tr><tr><td>10,000 add & delete randomly</td><td>68.35</td><td>14.63</td><td>8.46e-4</td></tr><tr><td>10,000 addMany</td><td>28.94</td><td>34.56</td><td>0.00</td></tr><tr><td>10,000 get</td><td>28.97</td><td>34.51</td><td>4.57e-4</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>rb-tree</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>100,000 add</td><td>89.12</td><td>11.22</td><td>0.00</td></tr><tr><td>100,000 add & delete randomly</td><td>219.00</td><td>4.57</td><td>0.00</td></tr><tr><td>100,000 getNode</td><td>105.74</td><td>9.46</td><td>6.09e-4</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>directed-graph</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000 addVertex</td><td>0.10</td><td>9902.14</td><td>9.12e-7</td></tr><tr><td>1,000 addEdge</td><td>6.11</td><td>163.67</td><td>1.29e-4</td></tr><tr><td>1,000 getVertex</td><td>0.05</td><td>2.16e+4</td><td>3.51e-7</td></tr><tr><td>1,000 getEdge</td><td>23.63</td><td>42.33</td><td>0.00</td></tr><tr><td>tarjan</td><td>222.85</td><td>4.49</td><td>0.01</td></tr><tr><td>tarjan all</td><td>223.79</td><td>4.47</td><td>0.00</td></tr><tr><td>topologicalSort</td><td>181.77</td><td>5.50</td><td>0.00</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>hash-map</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 set</td><td>1.00</td><td>1001.93</td><td>2.06e-5</td></tr><tr><td>10,000 set & get</td><td>1.51</td><td>661.53</td><td>2.65e-5</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>heap</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 add & pop</td><td>5.82</td><td>171.79</td><td>5.13e-5</td></tr><tr><td>10,000 fib add & pop</td><td>359.24</td><td>2.78</td><td>0.00</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>doubly-linked-list</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>214.50</td><td>4.66</td><td>0.05</td></tr><tr><td>1,000,000 unshift</td><td>205.89</td><td>4.86</td><td>0.05</td></tr><tr><td>1,000,000 unshift & shift</td><td>168.93</td><td>5.92</td><td>0.04</td></tr><tr><td>1,000,000 insertBefore</td><td>291.32</td><td>3.43</td><td>0.05</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>singly-linked-list</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 push & pop</td><td>215.82</td><td>4.63</td><td>0.02</td></tr><tr><td>10,000 insertBefore</td><td>248.56</td><td>4.02</td><td>0.01</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>max-priority-queue</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>10,000 refill & poll</td><td>8.85</td><td>113.01</td><td>1.90e-4</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>priority-queue</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>100,000 add & pop</td><td>104.23</td><td>9.59</td><td>0.00</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>deque</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>14.25</td><td>70.18</td><td>1.63e-4</td></tr><tr><td>1,000,000 push & pop</td><td>23.15</td><td>43.21</td><td>2.07e-4</td></tr><tr><td>1,000,000 push & shift</td><td>24.16</td><td>41.40</td><td>1.78e-4</td></tr><tr><td>1,000,000 unshift & shift</td><td>22.28</td><td>44.88</td><td>1.56e-4</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>queue</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>45.72</td><td>21.87</td><td>0.01</td></tr><tr><td>1,000,000 push & shift</td><td>81.75</td><td>12.23</td><td>0.00</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>stack</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>44.30</td><td>22.57</td><td>0.01</td></tr><tr><td>1,000,000 push & pop</td><td>49.69</td><td>20.12</td><td>0.01</td></tr></table></div>
</div><div class="json-to-html-collapse clearfix 0">
<div class='collapsible level0' ><span class='json-to-html-label'>trie</span></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>100,000 push</td><td>43.10</td><td>23.20</td><td>6.46e-4</td></tr><tr><td>100,000 getWords</td><td>85.45</td><td>11.70</td><td>0.00</td></tr></table></div>
<div class="content"><table style="display: table; width:100%; table-layout: fixed;"><tr><th>test name</th><th>time taken (ms)</th><th>executions per sec</th><th>sample deviation</th></tr><tr><td>1,000,000 push</td><td>13.49</td><td>74.16</td><td>1.40e-4</td></tr><tr><td>1,000,000 push & pop</td><td>22.48</td><td>44.48</td><td>0.00</td></tr><tr><td>1,000,000 push & shift</td><td>23.42</td><td>42.71</td><td>1.59e-4</td></tr><tr><td>1,000,000 unshift & shift</td><td>21.62</td><td>46.25</td><td>2.77e-4</td></tr></table></div>
</div>
[//]: # (No deletion!!! End of Replace Section)

View file

@ -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<K, V> {
readonly hashMap: HashMap<K, V>;
readonly iterateDirection: IterateDirection;
protected _node: HashMapLinkedNode<K, V>;
protected readonly _sentinel: HashMapLinkedNode<K, V>;
/**
* 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<K, V>,
sentinel: HashMapLinkedNode<K, V>,
hashMap: HashMap<K, V>,
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]>(<unknown>[]), {
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<K, V>(this._node, this._sentinel, this.hashMap, this.iterateDirection)
}
}
import { isObjOrFunc, rangeCheck } from '../../utils';
import { HashMapLinkedNode, IterableWithSizeOrLength } from '../../types';
export class HashMap<K = any, V = any> {
readonly OBJ_KEY_INDEX = Symbol('OBJ_KEY_INDEX');
@ -158,53 +39,6 @@ export class HashMap<K = any, V = any> {
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<K, V>(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<K, V>(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<K, V>(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<K, V>(this._sentinel, this._sentinel, this, IterateDirection.REVERSE);
}
/**
* Time Complexity: O(1)
* Space Complexity: O(1)
@ -231,6 +65,29 @@ export class HashMap<K = any, V = any> {
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<K = any, V = any> {
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<K, V>;
if (isObjectKey) {
const index = (<Record<symbol, number>>(<unknown>key))[this.OBJ_KEY_INDEX];
if (index === undefined) {
node = this._sentinel;
} else {
node = this._nodes[index];
}
} else {
node = this._orgMap[<string>(<unknown>key)] || this._sentinel;
}
return new HashMapIterator<K, V>(node, this._sentinel, this);
}
/**
* Time Complexity: O(1)
* Space Complexity: O(1)

View file

@ -40,7 +40,7 @@ export class Deque<E> {
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<E> {
this._firstInBucket = this._lastInBucket = this._bucketSize >> 1;
}
*begin(): Generator<E> {
/**
* The below function is a generator that yields elements from a collection one by one.
*/
* begin(): Generator<E> {
let index = 0;
while (index < this.size) {
yield this.getAt(index);
@ -169,15 +172,11 @@ export class Deque<E> {
}
}
*end(): Generator<E> {
let index = this.size;
while (index > 0) {
index--;
yield this.getAt(index);
}
}
*reverseBegin(): Generator<E> {
/**
* The function `reverseBegin()` is a generator that yields elements in reverse order starting from
* the last element.
*/
* reverseBegin(): Generator<E> {
let index = this.size - 1;
while (index >= 0) {
yield this.getAt(index);
@ -185,14 +184,6 @@ export class Deque<E> {
}
}
*reverseEnd(): Generator<E> {
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).

View file

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

View file

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