diff --git a/CHANGELOG.md b/CHANGELOG.md index c1b4023..44a902e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [5.1.0] - 2022-12-08 + +### Added +- ability to abort tree traversal using a callback. + ## [5.0.2] - 2022-08-21 ### Fixed diff --git a/README.md b/README.md index 26ac85e..d301b2a 100644 --- a/README.md +++ b/README.md @@ -232,7 +232,7 @@ employees.count(); // 7 ### traverseInOrder O(n) -traverses the tree in order (left-node-right). +traverses the tree in order (left-node-right). it also accepts an optional second param as a callback to abort traversal when it returns true. ```js nums.traverseInOrder((node) => console.log(node.getValue())); @@ -256,12 +256,23 @@ employees.traverseInOrder((node) => console.log(node.getValue())); { id: 80 } { id: 90 } */ + +let counter = 0; +const abortCb = () => counter > 1; +employees.traverseInOrder((node) => { + console.log(node.getValue()); + counter += 1; +}, abortCb); +/* + { id: 20 } + { id: 30 } +*/ ``` ### traversePreOrder O(n) -traverses the tree pre order (node-left-right). +traverses the tree pre order (node-left-right). it also accepts an optional second param as a callback to abort traversal when it returns true. ```js nums.traversePreOrder((node) => console.log(node.getValue())); @@ -285,12 +296,23 @@ employees.traversePreOrder((node) => console.log(node.getValue())); { id: 60 } { id: 90 } */ + +let counter = 0; +const abortCb = () => counter > 1; +employees.traversePreOrder((node) => { + console.log(node.getValue()); + counter += 1; +}, abortCb); +/* + { id: 50 } + { id: 30 } +*/ ``` ### traversePostOrder O(n) -traverses the tree post order (left-right-node). +traverses the tree post order (left-right-node). it also accepts an optional second param as a callback to abort traversal when it returns true. ```js nums.traversePostOrder((node) => console.log(node.getValue())); @@ -314,6 +336,17 @@ employees.traversePostOrder((node) => console.log(node.getValue())); { id: 80 } { id: 50 } */ + +let counter = 0; +const abortCb = () => counter > 1; +employees.traversePostOrder((node) => { + console.log(node.getValue()); + counter += 1; +}, abortCb); +/* + { id: 20 } + { id: 40 } +*/ ``` ### remove diff --git a/package.json b/package.json index 1af1e3f..433e4be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@datastructures-js/binary-search-tree", - "version": "5.0.2", + "version": "5.1.0", "description": "binary search tree & avl tree (self balancing tree) implementation in javascript", "main": "index.js", "types": "index.d.ts", diff --git a/src/binarySearchTree.d.ts b/src/binarySearchTree.d.ts index b03bc42..548a003 100644 --- a/src/binarySearchTree.d.ts +++ b/src/binarySearchTree.d.ts @@ -14,8 +14,8 @@ export class BinarySearchTree { root(): BinarySearchTreeNode | null; count(): number; remove(value: T): boolean; - traverseInOrder(cb: (node: BinarySearchTreeNode) => void): void; - traversePreOrder(cb: (node: BinarySearchTreeNode) => void): void; - traversePostOrder(cb: (node: BinarySearchTreeNode) => void): void; + traverseInOrder(cb: (node: BinarySearchTreeNode) => void, abortCb?: () => boolean): void; + traversePreOrder(cb: (node: BinarySearchTreeNode) => void, abortCb?: () => boolean): void; + traversePostOrder(cb: (node: BinarySearchTreeNode) => void, abortCb?: () => boolean): void; clear(): void; } diff --git a/src/binarySearchTree.js b/src/binarySearchTree.js index cf4d5a9..4a4bb4d 100644 --- a/src/binarySearchTree.js +++ b/src/binarySearchTree.js @@ -289,15 +289,17 @@ class BinarySearchTree { * Traverses the tree in-order (left-node-right) * @public * @param {function} cb + * @param {function} [abortCb] */ - traverseInOrder(cb) { + traverseInOrder(cb, abortCb) { if (typeof cb !== 'function') { throw new Error('.traverseInOrder expects a callback function'); } const traverseRecursive = (current) => { - if (current === null) return; + if (current === null || (abortCb && abortCb())) return; traverseRecursive(current.getLeft()); + if (abortCb && abortCb()) return; cb(current); traverseRecursive(current.getRight()); }; @@ -309,14 +311,15 @@ class BinarySearchTree { * Traverses the tree pre-order (node-left-right) * @public * @param {function} cb + * @param {function} [abortCb] */ - traversePreOrder(cb) { + traversePreOrder(cb, abortCb) { if (typeof cb !== 'function') { throw new Error('.traversePreOrder expects a callback function'); } const traverseRecursive = (current) => { - if (current === null) return; + if (current === null || (abortCb && abortCb())) return; cb(current); traverseRecursive(current.getLeft()); traverseRecursive(current.getRight()); @@ -329,16 +332,18 @@ class BinarySearchTree { * Traverses the tree post-order (left-right-node) * @public * @param {function} cb + * @param {function} [abortCb] */ - traversePostOrder(cb) { + traversePostOrder(cb, abortCb) { if (typeof cb !== 'function') { throw new Error('.traversePostOrder expects a callback function'); } const traverseRecursive = (current) => { - if (current === null) return; + if (current === null || (abortCb && abortCb())) return; traverseRecursive(current.getLeft()); traverseRecursive(current.getRight()); + if (abortCb && abortCb()) return; cb(current); }; diff --git a/test/binarySearchTree.test.js b/test/binarySearchTree.test.js index f15ed77..c663416 100644 --- a/test/binarySearchTree.test.js +++ b/test/binarySearchTree.test.js @@ -123,6 +123,16 @@ describe('BinarySearchTree tests', () => { bst.traverseInOrder((node) => keys.push(node.getValue())); expect(keys).to.deep.equal([20, 30, 40, 50, 60, 80, 90]); }); + + it('traverse in order and allow aborting traversal', () => { + const keys = []; + let counter = 0; + bst.traverseInOrder((node) => { + keys.push(node.getValue()); + counter += 1; + }, () => counter > 2); + expect(keys).to.deep.equal([20, 30, 40]); + }); }); describe('.traversePreOrder(cb)', () => { @@ -131,6 +141,16 @@ describe('BinarySearchTree tests', () => { bst.traversePreOrder((node) => keys.push(node.getValue())); expect(keys).to.deep.equal([50, 30, 20, 40, 80, 60, 90]); }); + + it('traverse pre order and allow aborting traversal', () => { + const keys = []; + let counter = 0; + bst.traversePreOrder((node) => { + keys.push(node.getValue()); + counter += 1; + }, () => counter > 2); + expect(keys).to.deep.equal([50, 30, 20]); + }); }); describe('.traversePostOrder(cb)', () => { @@ -139,6 +159,16 @@ describe('BinarySearchTree tests', () => { bst.traversePostOrder((node) => keys.push(node.getValue())); expect(keys).to.deep.equal([20, 40, 30, 60, 90, 80, 50]); }); + + it('traverse post order and allow aborting traversal', () => { + const keys = []; + let counter = 0; + bst.traversePostOrder((node) => { + keys.push(node.getValue()); + counter += 1; + }, () => counter > 2); + expect(keys).to.deep.equal([20, 40, 30]); + }); }); describe('.remove(value)', () => {