From dd32e35d1a5904787d4333a2fb7d45cb4ff8ce19 Mon Sep 17 00:00:00 2001 From: Bakhtier Gaibulloev Date: Fri, 6 Jun 2025 08:59:23 +0200 Subject: [PATCH 1/2] Add iterable support to lists --- DEVELOPMENT_PLAN.md | 2 +- src/List/DoubleLinkedList.ts | 11 +++++++++++ src/List/LinkedList.ts | 11 +++++++++++ tests/List/DoubleLinkedList.spec.ts | 13 +++++++++++++ tests/List/LinkedList.spec.ts | 13 +++++++++++++ 5 files changed, 49 insertions(+), 1 deletion(-) diff --git a/DEVELOPMENT_PLAN.md b/DEVELOPMENT_PLAN.md index c366785..2494b3f 100644 --- a/DEVELOPMENT_PLAN.md +++ b/DEVELOPMENT_PLAN.md @@ -35,5 +35,5 @@ This document outlines possible improvements and future developments for the `TS - **Performance Benchmarks**: Add benchmarks to evaluate and optimize the performance of the data structures. - **Browser Support**: Provide build targets that work in browser environments, possibly using bundlers like Rollup or Webpack. -- **Typed Iterators**: Implement iterator interfaces for data structures to integrate seamlessly with ES6 iteration protocols. +- **Typed Iterators**: Implement iterator interfaces for data structures to integrate seamlessly with ES6 iteration protocols. *(Implemented)* diff --git a/src/List/DoubleLinkedList.ts b/src/List/DoubleLinkedList.ts index e71aef9..bb0f02e 100644 --- a/src/List/DoubleLinkedList.ts +++ b/src/List/DoubleLinkedList.ts @@ -121,6 +121,17 @@ export class DoubleLinkedList { return result; } + /** + * Enables iteration over the list using ES6 iteration protocols. + */ + public *[Symbol.iterator](): IterableIterator { + let node = this._head; + while (node !== null) { + yield node.getValue(); + node = node.getNext(); + } + } + /** * Inserts given value to the DoubleLinkedList and returns updated DoubleLinkedList. * */ diff --git a/src/List/LinkedList.ts b/src/List/LinkedList.ts index c7d0ab9..1b4df59 100644 --- a/src/List/LinkedList.ts +++ b/src/List/LinkedList.ts @@ -120,6 +120,17 @@ export class LinkedList { return result; } + /** + * Enables iteration over the list using ES6 iteration protocols. + */ + public *[Symbol.iterator](): IterableIterator { + let node = this._head; + while (node !== null) { + yield node.getValue(); + node = node.getNext(); + } + } + /** * Inserts given value to the linkedList and returns updated LinkedList * */ diff --git a/tests/List/DoubleLinkedList.spec.ts b/tests/List/DoubleLinkedList.spec.ts index b1d223e..f133474 100644 --- a/tests/List/DoubleLinkedList.spec.ts +++ b/tests/List/DoubleLinkedList.spec.ts @@ -79,6 +79,19 @@ describe('DoubleLinkedList', () => { }); + it('should be iterable with for-of', () => { + doubleLinkedList.append(1); + doubleLinkedList.append(2); + doubleLinkedList.append(3); + doubleLinkedList.append(4); + + const collected: number[] = []; + for (const value of doubleLinkedList) { + collected.push(value); + } + expect(collected).to.deep.equal([1, 2, 3, 4]); + }); + it('getLast', () => { doubleLinkedList.append(1); doubleLinkedList.append(2); diff --git a/tests/List/LinkedList.spec.ts b/tests/List/LinkedList.spec.ts index 75244c6..3482838 100644 --- a/tests/List/LinkedList.spec.ts +++ b/tests/List/LinkedList.spec.ts @@ -69,6 +69,19 @@ describe('LinkedList', () => { }); + it('should be iterable with for-of', () => { + linkedList.append(1); + linkedList.append(2); + linkedList.append(3); + linkedList.append(4); + + const collected: number[] = []; + for (const value of linkedList) { + collected.push(value); + } + expect(collected).to.deep.equal([1, 2, 3, 4]); + }); + it('getLast', () => { linkedList.append(1); linkedList.append(2); From 09c93a78e6f450e283a1ecd64ce6062b19caadb0 Mon Sep 17 00:00:00 2001 From: Bakhtier Gaibulloev Date: Fri, 6 Jun 2025 09:21:13 +0200 Subject: [PATCH 2/2] Add iterable support for all structures --- src/AVLTree/AVLTree.ts | 15 +++++++++++++++ src/BinaryTree/BinaryTree.ts | 15 +++++++++++++++ src/Dictionary/Dictionary.ts | 9 +++++++++ src/Graph/Graph.ts | 9 +++++++++ src/HashMap/HashMap.ts | 9 +++++++++ src/PriorityQueue/PriorityQueue.ts | 9 +++++++++ src/Queue/Queue.ts | 9 +++++++++ src/Stack/Stack.ts | 9 +++++++++ src/Tree.ts | 15 +++++++++++++++ tests/AVLTree/AVLTree.spec.ts | 10 ++++++++++ tests/BinaryTree/BinaryTree.spec.ts | 9 +++++++++ tests/Dictionary/Dictionary.spec.ts | 13 +++++++++++++ tests/Graph/Graph.spec.ts | 10 ++++++++++ tests/HashMap/HashMap.spec.ts | 13 +++++++++++++ tests/PriorityQueue/PriorityQueue.spec.ts | 12 ++++++++++++ tests/Queue/Queue.spec.ts | 12 ++++++++++++ tests/Stack/Stack.spec.ts | 12 ++++++++++++ tests/Tree/Tree.spec.ts | 11 +++++++++++ 18 files changed, 201 insertions(+) diff --git a/src/AVLTree/AVLTree.ts b/src/AVLTree/AVLTree.ts index e8681f6..25e602e 100644 --- a/src/AVLTree/AVLTree.ts +++ b/src/AVLTree/AVLTree.ts @@ -62,6 +62,21 @@ export class AVLTree { return result; } + /** + * Enables iteration over the tree values in-order. + */ + public *[Symbol.iterator](): IterableIterator { + function* traverse(node: AVLTreeNode | null): IterableIterator { + if (node === null) { + return; + } + yield* traverse(node.getLeft()); + yield node.getValue(); + yield* traverse(node.getRight()); + } + yield* traverse(this.root); + } + private insertNode(node: AVLTreeNode | null, value: T): AVLTreeNode { if (node === null) { return new AVLTreeNode(value); diff --git a/src/BinaryTree/BinaryTree.ts b/src/BinaryTree/BinaryTree.ts index 1338d8e..8dfecf0 100644 --- a/src/BinaryTree/BinaryTree.ts +++ b/src/BinaryTree/BinaryTree.ts @@ -316,6 +316,21 @@ export class BinaryTree { return this.inOrderTraversal(); } + /** + * Enables iteration over tree values in-order. + */ + public *[Symbol.iterator](): IterableIterator { + function* traverse(node: BinaryTreeNode | null): IterableIterator { + if (node === null || node.getValue() === null) { + return; + } + yield* traverse(node.getLeft()); + yield node.getValue(); + yield* traverse(node.getRight()); + } + yield* traverse(this._root); + } + /** * Returns a string representation of the tree. * @return string diff --git a/src/Dictionary/Dictionary.ts b/src/Dictionary/Dictionary.ts index 7006bbc..47c347d 100644 --- a/src/Dictionary/Dictionary.ts +++ b/src/Dictionary/Dictionary.ts @@ -170,6 +170,15 @@ export class Dictionary { }); } + /** + * Enables iteration over key-value pairs in insertion order. + */ + public *[Symbol.iterator](): IterableIterator<{ key: string; value: T }> { + for (const key of this.keys()) { + yield { key, value: this.items[key] }; + } + } + // ========================================================================================================================================================= // Private methods // ========================================================================================================================================================= diff --git a/src/Graph/Graph.ts b/src/Graph/Graph.ts index aebdee8..69bd5f9 100644 --- a/src/Graph/Graph.ts +++ b/src/Graph/Graph.ts @@ -147,4 +147,13 @@ export class Graph { } return path; } + /** + * Enables iteration over all vertices in the graph. + */ + public *[Symbol.iterator](): IterableIterator { + for (const key of this.adjacency.keys()) { + yield key; + } + } + } diff --git a/src/HashMap/HashMap.ts b/src/HashMap/HashMap.ts index 8e18c3f..0bdedef 100644 --- a/src/HashMap/HashMap.ts +++ b/src/HashMap/HashMap.ts @@ -135,5 +135,14 @@ export class HashMap { public clear(): void { this.map.clear(); } + /** + * Enables iteration over key-value pairs in insertion order. + */ + public *[Symbol.iterator](): IterableIterator<[K, V]> { + for (const entry of this.map.entries()) { + yield entry; + } + } + } diff --git a/src/PriorityQueue/PriorityQueue.ts b/src/PriorityQueue/PriorityQueue.ts index 141fe08..925364b 100644 --- a/src/PriorityQueue/PriorityQueue.ts +++ b/src/PriorityQueue/PriorityQueue.ts @@ -42,6 +42,15 @@ export class PriorityQueue { return this.heap.map(item => item.value); } + /** + * Iterates over the queue values in heap order. + */ + public *[Symbol.iterator](): IterableIterator { + for (const item of this.heap) { + yield item.value; + } + } + private bubbleUp(index: number): void { const element = this.heap[index]; while (index > 0) { diff --git a/src/Queue/Queue.ts b/src/Queue/Queue.ts index b1eb0f3..f7bf009 100644 --- a/src/Queue/Queue.ts +++ b/src/Queue/Queue.ts @@ -70,6 +70,15 @@ export class Queue { return this._store; } + /** + * Enables iteration over queue values from front to back. + */ + public *[Symbol.iterator](): IterableIterator { + for (const item of this._store) { + yield item; + } + } + // ========================================================================================================================================================= // Private methods // ========================================================================================================================================================= diff --git a/src/Stack/Stack.ts b/src/Stack/Stack.ts index 584464b..4f71f11 100644 --- a/src/Stack/Stack.ts +++ b/src/Stack/Stack.ts @@ -72,6 +72,15 @@ export class Stack { return this._store; } + /** + * Enables iteration over stack values from bottom to top. + */ + public *[Symbol.iterator](): IterableIterator { + for (const item of this._store) { + yield item; + } + } + // ========================================================================================================================================================= // Private methods // ========================================================================================================================================================= diff --git a/src/Tree.ts b/src/Tree.ts index 63d5ee9..cbfe92f 100644 --- a/src/Tree.ts +++ b/src/Tree.ts @@ -89,4 +89,19 @@ export class Tree { node.getChildren().forEach(child => queue.push(child)); } } + + /** + * Enables iteration over the tree nodes in level order. + */ + public *[Symbol.iterator](): IterableIterator { + if (this.root === null) { + return; + } + const queue: TreeNode[] = [this.root]; + while (queue.length > 0) { + const node = queue.shift()!; + yield node.getValue(); + queue.push(...node.getChildren()); + } + } } diff --git a/tests/AVLTree/AVLTree.spec.ts b/tests/AVLTree/AVLTree.spec.ts index deecbb9..cd87726 100644 --- a/tests/AVLTree/AVLTree.spec.ts +++ b/tests/AVLTree/AVLTree.spec.ts @@ -26,4 +26,14 @@ describe('AVLTree', () => { [4, 2, 6, 1, 3, 5, 7].forEach(v => tree.insert(v)); expect(tree.toArray()).to.deep.equal([1,2,3,4,5,6,7]); }); + + it('should be iterable with for-of', () => { + const tree = new AVLTree(); + [3,1,4].forEach(v => tree.insert(v)); + const result: number[] = []; + for (const v of tree) { + result.push(v); + } + expect(result).to.deep.equal([1,3,4]); + }); }); diff --git a/tests/BinaryTree/BinaryTree.spec.ts b/tests/BinaryTree/BinaryTree.spec.ts index 74270df..cda078e 100644 --- a/tests/BinaryTree/BinaryTree.spec.ts +++ b/tests/BinaryTree/BinaryTree.spec.ts @@ -456,6 +456,15 @@ describe('BinaryTree', () => { expect(tree.toArray()).to.deep.equal([3, 5, 8, 10, 15]); }); + it('should be iterable with for-of', () => { + const tree = new BinaryTree(); + [10, 5, 15].forEach(v => tree.insert(v)); + const result: number[] = []; + for (const value of tree) { + result.push(value); + } + expect(result).to.deep.equal([5, 10, 15]); + }); }); }); diff --git a/tests/Dictionary/Dictionary.spec.ts b/tests/Dictionary/Dictionary.spec.ts index 6d51ed2..c0accd9 100644 --- a/tests/Dictionary/Dictionary.spec.ts +++ b/tests/Dictionary/Dictionary.spec.ts @@ -84,6 +84,19 @@ describe('Dictionary', () => { ]); }); + it('should be iterable with for-of', () => { + dictionary.put('a', 1); + dictionary.put('b', 2); + const entries: { key: string; value: number }[] = []; + for (const item of dictionary) { + entries.push(item); + } + expect(entries).to.deep.equal([ + { key: 'a', value: 1 }, + { key: 'b', value: 2 }, + ]); + }); + it('setValue()', () => { dictionary.put('key1', 1); diff --git a/tests/Graph/Graph.spec.ts b/tests/Graph/Graph.spec.ts index f288765..c6e3ca4 100644 --- a/tests/Graph/Graph.spec.ts +++ b/tests/Graph/Graph.spec.ts @@ -36,4 +36,14 @@ describe('Graph', () => { const path = graph.shortestPath('A', 'C'); expect(path).to.deep.equal(['A', 'B', 'C']); }); + + it('should be iterable with for-of', () => { + graph.addVertex('X'); + graph.addVertex('Y'); + const nodes: string[] = []; + for (const v of graph) { + nodes.push(v); + } + expect(new Set(nodes)).to.deep.equal(new Set(['X', 'Y'])); + }); }); diff --git a/tests/HashMap/HashMap.spec.ts b/tests/HashMap/HashMap.spec.ts index 846ecfd..03f84de 100644 --- a/tests/HashMap/HashMap.spec.ts +++ b/tests/HashMap/HashMap.spec.ts @@ -212,4 +212,17 @@ describe('HashMap', () => { expect(map.isEmpty()).to.be.true; }); }); + + it('should be iterable with for-of', () => { + map.put('a', 1); + map.put('b', 2); + const entries: Array<[string, number]> = []; + for (const pair of map) { + entries.push(pair); + } + expect(entries).to.deep.include.members([ + ['a', 1], + ['b', 2], + ]); + }); }); diff --git a/tests/PriorityQueue/PriorityQueue.spec.ts b/tests/PriorityQueue/PriorityQueue.spec.ts index fc43daa..a09c37b 100644 --- a/tests/PriorityQueue/PriorityQueue.spec.ts +++ b/tests/PriorityQueue/PriorityQueue.spec.ts @@ -31,4 +31,16 @@ describe('PriorityQueue', () => { expect(queue.peek()).equal(null); expect(queue.size()).equal(0); }); + + it('should be iterable with for-of', () => { + queue.push('low', 5); + queue.push('high', 1); + queue.push('mid', 3); + + const collected: string[] = []; + for (const value of queue) { + collected.push(value); + } + expect(new Set(collected)).to.deep.equal(new Set(['low', 'high', 'mid'])); + }); }); diff --git a/tests/Queue/Queue.spec.ts b/tests/Queue/Queue.spec.ts index dae02ab..df389c2 100644 --- a/tests/Queue/Queue.spec.ts +++ b/tests/Queue/Queue.spec.ts @@ -46,6 +46,18 @@ describe('Queue', () => { }); + it('should be iterable with for-of', () => { + queue.push(1); + queue.push(2); + queue.push(3); + + const collected: number[] = []; + for (const value of queue) { + collected.push(value); + } + expect(collected).to.deep.equal([1, 2, 3]); + }); + it('size', () => { queue.push(1); diff --git a/tests/Stack/Stack.spec.ts b/tests/Stack/Stack.spec.ts index b742ba7..f4a6d31 100644 --- a/tests/Stack/Stack.spec.ts +++ b/tests/Stack/Stack.spec.ts @@ -52,6 +52,18 @@ describe('Stack', () => { }); + it('should be iterable with for-of', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + const collected: number[] = []; + for (const value of stack) { + collected.push(value); + } + expect(collected).to.deep.equal([1, 2, 3]); + }); + it('size', () => { stack.push(1); diff --git a/tests/Tree/Tree.spec.ts b/tests/Tree/Tree.spec.ts index cd0a5ec..c9b4049 100644 --- a/tests/Tree/Tree.spec.ts +++ b/tests/Tree/Tree.spec.ts @@ -80,4 +80,15 @@ describe('Tree', () => { expect(visitedNodes).to.deep.equal([1, 2, 3, 4, 5]); }); }); + + it('should be iterable with for-of', () => { + const root = tree.getRoot(); + tree.addChild(root, 2); + tree.addChild(root, 3); + const collected: number[] = []; + for (const val of tree) { + collected.push(val); + } + expect(collected).to.deep.equal([1, 2, 3]); + }); });