/** * A simple tree data structure. */ export class TreeNode { private _value: NodeT; private _children: TreeNode[]; private _parent?: TreeNode; constructor(value: NodeT) { this._value = value; this._children = []; this._parent = undefined; } /** * Creates a TreeNode root node. * @param value Root node value. * @returns TreeNode root node. */ public static createRoot(value: NodeT): TreeNode { return new TreeNode(value); } /** * Adds value node to parent TreeNode and returns child TreeNode. * @param value Child node value. * @returns Child tree node. */ public addChild(value: NodeT): TreeNode { let childTreeNode = new TreeNode(value); childTreeNode._parent = this; let length = this._children.push(childTreeNode); return this._children[length-1]; } /** * Gets value of node. */ public get value(): NodeT { return this._value; } /** * Sets value of node. */ public set value(value: NodeT) { this._value = value; } /** * Gets TreeNode parent of current TreeNode. * @returns Parent TreeNode of current TreeNode. */ public parent(): TreeNode { return this._parent||this; } /** * Gets root TreeNode node of tree. * @returns Root TreeNode node of tree. */ public root(): TreeNode { if (this.parent() === this) { return this; } else { return this.parent().root(); } } /** * Sets new parent of node and returns this TreeNode node. * @param parent Parent of node. * @returns This TreeNode node. */ public setParent(parent: TreeNode) { this._parent = parent; return this; } /** * Gets children of this TreeNode node as an Array of node values. */ public get children(): TreeNode[] { return this._children; } /** * Checks to see if the current node value matches the predicate. * @param predicate Predicate determines truthiness of the match. * @returns Boolean indicating if this tree node value matches the predicate. */ public is(predicate: (value: NodeT) => boolean): boolean { const result = predicate(this.value); return result; } /** * Performs shallow search for the predicate among itself and its TreeNode children, checking the values against the predicate. * @param predicate Predicate determines truthiness of the match. * @returns The TreeNode matching the predicate, if it exists. Otherwise, undefined. */ public findOne(predicate: (value: NodeT) => boolean): TreeNode | undefined { const initialIs = this.is(predicate); if (initialIs) { return this; } else { for (const child of this.children) { const result = child.is(predicate); if (result) { return child; } } } } /** * Performs shallow search for the predicates among itself and its TreeNode children, checking the values against the predicate. * @param predicate Predicate determines truthiness of the matches. * @returns An array of TreeNodes matching the predicate, or an empty array if no predicates exist. */ public findAll(predicate: (value: NodeT) => boolean): TreeNode[] { let results: TreeNode[] = []; if (this.is(predicate)) { results.push(this); } for (const child of this.children) { if (child.is(predicate)) { results.push(child); } } return results; } /** * Performs deep search for value among itself and its TreeNode children, checking the values against the predicate. * @param predicate Predicate determines truthiness of the match. * @returns The TreeNode matching the predicate, if it exists. Otherwise, undefined. */ public findOneRecursive(predicate: (value: NodeT) => boolean): TreeNode | undefined { const initialFindOne = this.findOne(predicate); if (initialFindOne) { return initialFindOne; } if (this.children.length) { for (let child of this.children) { let result = child.findOneRecursive(predicate); if (result) { return result; } } } return undefined; } /** * Performs deep search for value among TreeNode children, checking the values against the predicate. * @param predicate Predicate determines truthiness of the matches. * @returns The TreeNode matching the predicate, if it exists. Otherwise, undefined. */ public findAllRecursive(predicate: (value: NodeT) => boolean): TreeNode[] { let results: TreeNode[] = []; if (this.is(predicate)) { results.push(this); } if (this.children.length) { for (let child of this.children) { let childResults = child.findAllRecursive(predicate); if (childResults.length) { results.push(...childResults); } } } return results; } /** * Flattens the tree into an array of TreeNodes. */ public getAllNodes(): TreeNode[] { let nodes: TreeNode[] = []; nodes.push(this); for (let child of this.children) { nodes.push(...child.getAllNodes()); } return nodes; } }