diff --git a/README.md b/README.md index d301b2a..b465851 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,9 @@ Binary Search Tree & AVL Tree (Self Balancing Tree) implementation in javascript * [constructor](#constructor) * [insert](#insert) * [has](#has) + * [hasKey](#haskey) * [find](#find) + * [findKey](#findkey) * [min](#min) * [max](#max) * [lowerBound (floor)](#lowerbound-floor) @@ -70,6 +72,8 @@ the compare function must return a number for the 3 cases: There is already a default compare function for primitive values (number, string). +constructor also accepts an options param, where the comparison key prob name can be passed for object types in order to search by that key directly using findKey and hasKey. + ##### JS ###### BinarySearchTree ```js @@ -80,7 +84,7 @@ const employees = new BinarySearchTree((a, b) => a.id - b.id); ###### AvlTree ```js const nums = new AvlTree(); -const employees = new AvlTree((a, b) => a.id - b.id); +const employees = new AvlTree((a, b) => a.id - b.id, { key: 'id' }); ``` ##### TS @@ -93,13 +97,13 @@ interface IEmployee { ###### BinarySearchTree ```js const nums = new BinarySearchTree(); -const employees = new BinarySearchTree((a, b) => a.id - b.id); +const employees = new BinarySearchTree((a, b) => a.id - b.id, { key: 'id' }); ``` ###### AvlTree ```js const nums = new AvlTree(); -const employees = new AvlTree((a, b) => a.id - b.id); +const employees = new AvlTree((a, b) => a.id - b.id, { key: 'id' }); ``` ### insert @@ -140,6 +144,16 @@ employees.has({ id: 50 }); // true employees.has({ id: 100 }); // false ``` +### hasKey +O(log(n)) + +checks if a value exists by its key if the node's key prob is provided in the constructor. + +```js +employees.has(50); // true +employees.has(100); // false +``` + ### find O(log(n)) @@ -153,6 +167,16 @@ employees.find({ id: 60 }).getValue(); // { id: 60 } employees.find({ id: 100 }); // null ``` +### findKey +O(log(n)) + +finds a node by its key if the node's key prob is provided in the constructor. + +```js +employees.findKey(60).getValue(); // { id: 60 } +employees.find(100); // null +``` + ### min O(log(n)) diff --git a/src/avlTree.d.ts b/src/avlTree.d.ts index 6295bc8..64b1e74 100644 --- a/src/avlTree.d.ts +++ b/src/avlTree.d.ts @@ -2,9 +2,10 @@ import { BinarySearchTree } from './binarySearchTree'; import { AvlTreeNode } from './avlTreeNode'; export class AvlTree extends BinarySearchTree { - constructor(compare?: (a: T, b: T) => number); + constructor(compare?: (a: T, b: T) => number, options?: { key: string }); insert(value: T): AvlTree; find(value: T): AvlTreeNode | null; + findKey(key: number|string): AvlTreeNode | null; max(node?: AvlTreeNode): AvlTreeNode | null; min(node?: AvlTreeNode): AvlTreeNode | null; lowerBound(value: T, includeEqual?: boolean): AvlTreeNode | null; diff --git a/src/avlTree.js b/src/avlTree.js index 1caab84..0b40bc2 100644 --- a/src/avlTree.js +++ b/src/avlTree.js @@ -12,12 +12,12 @@ const { AvlTreeNode } = require('./avlTreeNode'); * @extends BinarySearchTree */ class AvlTree extends BinarySearchTree { - constructor(compare) { + constructor(compare, options) { if (compare && typeof compare !== 'function') { throw new Error('AvlTree constructor expects a compare function'); } - super(compare); + super(compare, options); } /** diff --git a/src/binarySearchTree.d.ts b/src/binarySearchTree.d.ts index 548a003..ae1972a 100644 --- a/src/binarySearchTree.d.ts +++ b/src/binarySearchTree.d.ts @@ -1,10 +1,12 @@ import { BinarySearchTreeNode } from './binarySearchTreeNode'; export class BinarySearchTree { - constructor(compare?: (a: T, b: T) => number); + constructor(compare?: (a: T, b: T) => number, options?: { key: string }); insert(value: T): BinarySearchTree; has(value: T): boolean; + hasKey(key: number|string): boolean; find(value: T): BinarySearchTreeNode | null; + findKey(key: number|string): BinarySearchTreeNode | null; max(node?: BinarySearchTreeNode): BinarySearchTreeNode | null; min(node?: BinarySearchTreeNode): BinarySearchTreeNode | null; lowerBound(value: T, includeEqual?: boolean): BinarySearchTreeNode | null; diff --git a/src/binarySearchTree.js b/src/binarySearchTree.js index 4a4bb4d..f4fbbbd 100644 --- a/src/binarySearchTree.js +++ b/src/binarySearchTree.js @@ -15,12 +15,13 @@ const defaultCompare = (a, b) => { * @class BinarySearchTree */ class BinarySearchTree { - constructor(compare) { + constructor(compare, options) { if (compare && typeof compare !== 'function') { throw new Error('BinarySearchTree constructor expects a compare function'); } this._compare = compare || defaultCompare; + this._options = options || {}; this._root = null; this._count = 0; } @@ -65,9 +66,9 @@ class BinarySearchTree { } /** - * Checks if a value exists in the tree by its key + * Checks if a value exists in the tree by its value * @public - * @param {number|string} key + * @param {number|string|object} value * @return {boolean} */ has(value) { @@ -84,9 +85,22 @@ class BinarySearchTree { } /** - * Finds a node by its key + * Checks if a value exists in the tree by its key * @public * @param {number|string} key + * @return {boolean} + */ + hasKey(key) { + if (this._options.key === undefined || this._options.key === null) { + throw new Error('Missing key prop name in constructor options'); + } + return this.has({ [this._options.key]: key }); + } + + /** + * Finds a node by its value + * @public + * @param {number|string|object} value * @return {BinarySearchTreeNode} */ find(value) { @@ -102,6 +116,19 @@ class BinarySearchTree { return findRecursive(this._root); } + /** + * Finds a node by its key + * @public + * @param {number|string} key + * @return {BinarySearchTreeNode} + */ + findKey(key) { + if (this._options.key === undefined || this._options.key === null) { + throw new Error('Missing key prop name in constructor options'); + } + return this.find({ [this._options.key]: key }); + } + /** * Finds the node with max key (most right) in the tree * @public diff --git a/test/binarySearchTree.test.js b/test/binarySearchTree.test.js index c663416..1a8ea37 100644 --- a/test/binarySearchTree.test.js +++ b/test/binarySearchTree.test.js @@ -36,7 +36,7 @@ describe('BinarySearchTree tests', () => { }); describe('.has(value)', () => { - it('checks if a node exists by key', () => { + it('checks if a node exists by value', () => { expect(bst.has(50)).to.equal(true); expect(bst.has(80)).to.equal(true); expect(bst.has(30)).to.equal(true); @@ -48,8 +48,23 @@ describe('BinarySearchTree tests', () => { }); }); + describe('.has(key)', () => { + it('checks if a node exists by key', () => { + const testTree = new BinarySearchTree((a, b) => a.id - b.id, { key: 'id' }); + testTree.insert({ id: 1, name: 'a' }); + testTree.insert({ id: 2, name: 'b' }); + testTree.insert({ id: 3, name: 'c' }); + expect(testTree.has({ id: 1 })).to.equal(true); + expect(testTree.has({ id: 2 })).to.equal(true); + expect(testTree.has({ id: 3 })).to.equal(true); + expect(testTree.hasKey(1)).to.equal(true); + expect(testTree.hasKey(2)).to.equal(true); + expect(testTree.hasKey(3)).to.equal(true); + }); + }); + describe('.find(value)', () => { - it('should search a node by its key in the tree', () => { + it('should search a node by its value in the tree', () => { expect(bst.find(50)).to.be.instanceof(BinarySearchTreeNode); expect(bst.find(80)).to.be.instanceof(BinarySearchTreeNode); expect(bst.find(30)).to.be.instanceof(BinarySearchTreeNode); @@ -61,6 +76,21 @@ describe('BinarySearchTree tests', () => { }); }); + describe('.findKey(key)', () => { + it('should search a node by its key in the tree', () => { + const testTree = new BinarySearchTree((a, b) => a.id - b.id, { key: 'id' }); + testTree.insert({ id: 1, name: 'a' }); + testTree.insert({ id: 2, name: 'b' }); + testTree.insert({ id: 3, name: 'c' }); + expect(testTree.find({ id: 1 }).getValue()).to.eql({ id: 1, name: 'a' }); + expect(testTree.find({ id: 2 }).getValue()).to.eql({ id: 2, name: 'b' }); + expect(testTree.find({ id: 3 }).getValue()).to.eql({ id: 3, name: 'c' }); + expect(testTree.findKey(1).getValue()).to.eql({ id: 1, name: 'a' }); + expect(testTree.findKey(2).getValue()).to.eql({ id: 2, name: 'b' }); + expect(testTree.findKey(3).getValue()).to.eql({ id: 3, name: 'c' }); + }); + }); + describe('.max()', () => { it('get the node with max key', () => { const max = bst.max();