175 lines
5.6 KiB
TypeScript
175 lines
5.6 KiB
TypeScript
/**
|
|
* A simple tree data structure.
|
|
*/
|
|
export class TreeNode<NodeT> {
|
|
private _value: NodeT;
|
|
private _children: TreeNode<NodeT>[];
|
|
private _parent?: TreeNode<NodeT>;
|
|
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<NodeT>(value: NodeT): TreeNode<NodeT> {
|
|
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<NodeT> {
|
|
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<NodeT> {
|
|
return this._parent||this;
|
|
}
|
|
/**
|
|
* Gets root TreeNode node of tree.
|
|
* @returns Root TreeNode node of tree.
|
|
*/
|
|
public root(): TreeNode<NodeT> {
|
|
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<NodeT>) {
|
|
this._parent = parent;
|
|
return this;
|
|
}
|
|
/**
|
|
* Gets children of this TreeNode node as an Array of node values.
|
|
*/
|
|
public get children(): TreeNode<NodeT>[] {
|
|
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<NodeT> | 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<NodeT>[] {
|
|
let results: TreeNode<NodeT>[] = [];
|
|
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<NodeT> | 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<NodeT>[] {
|
|
let results: TreeNode<NodeT>[] = [];
|
|
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<NodeT>[] {
|
|
let nodes: TreeNode<NodeT>[] = [];
|
|
nodes.push(this);
|
|
for (let child of this.children) {
|
|
nodes.push(...child.getAllNodes());
|
|
}
|
|
return nodes;
|
|
}
|
|
}
|