feat: implement Tree

This commit is contained in:
vsavkin 2016-05-20 13:56:52 -07:00
parent 01111a1122
commit 37c5320e33
3 changed files with 120 additions and 0 deletions

View File

@ -0,0 +1,69 @@
export class Tree<T> {
/** @internal */
_root: TreeNode<T>;
constructor(root: TreeNode<T>) { this._root = root; }
get root(): T { return this._root.value; }
parent(t: T): T {
const p = this.pathFromRoot(t);
return p.length > 1 ? p[p.length - 2] : null;
}
children(t: T): T[] {
const n = _findNode(t, this._root);
return n ? n.children.map(t => t.value) : null;
}
firstChild(t: T): T {
const n = _findNode(t, this._root);
return n && n.children.length > 0 ? n.children[0].value : null;
}
pathFromRoot(t: T): T[] { return _findPath(t, this._root, []).map(s => s.value); }
contains(tree: Tree<T>): boolean { return _contains(this._root, tree._root); }
}
export function rootNode<T>(tree: Tree<T>): TreeNode<T> {
return tree._root;
}
function _findNode<T>(expected: T, c: TreeNode<T>): TreeNode<T> {
if (expected === c.value) return c;
for (let cc of c.children) {
const r = _findNode(expected, cc);
if (r) return r;
}
return null;
}
function _findPath<T>(expected: T, c: TreeNode<T>, collected: TreeNode<T>[]): TreeNode<T>[] {
collected.push(c);
if (expected === c.value) return collected;
for (let cc of c.children) {
const cloned = collected.slice(0);
const r = _findPath(expected, cc, cloned);
if (r) return r;
}
return null;
}
function _contains<T>(tree: TreeNode<T>, subtree: TreeNode<T>): boolean {
if (tree.value !== subtree.value) return false;
for (let subtreeNode of subtree.children) {
const s = tree.children.filter(child => child.value === subtreeNode.value);
if (s.length === 0) return false;
if (!_contains(s[0], subtreeNode)) return false;
}
return true;
}
export class TreeNode<T> {
constructor(public value: T, public children: TreeNode<T>[]) {}
}

View File

@ -0,0 +1,50 @@
import {Tree, TreeNode} from '../src/tree';
describe('tree', () => {
it("should return the root of the tree", () => {
const t = new Tree<any>(new TreeNode<number>(1, []));
expect(t.root).toEqual(1);
});
it("should return the parent of a node", () => {
const t = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(2, [])]));
expect(t.parent(1)).toEqual(null);
expect(t.parent(2)).toEqual(1);
});
it("should return the children of a node", () => {
const t = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(2, [])]));
expect(t.children(1)).toEqual([2]);
expect(t.children(2)).toEqual([]);
});
it("should return the first child of a node", () => {
const t = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(2, [])]));
expect(t.firstChild(1)).toEqual(2);
expect(t.firstChild(2)).toEqual(null);
});
it("should return the path to the root", () => {
const t = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(2, [])]));
expect(t.pathFromRoot(2)).toEqual([1, 2]);
});
describe("contains", () => {
it("should work", () => {
const tree = new Tree<any>(
new TreeNode<number>(1, [new TreeNode<number>(2, []), new TreeNode<number>(3, [])]));
const subtree1 = new Tree<any>(new TreeNode<number>(1, []));
const subtree2 = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(2, [])]));
const subtree3 = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(3, [])]));
const notSubtree1 = new Tree<any>(new TreeNode<number>(1, [new TreeNode<number>(4, [])]));
const notSubtree2 = new Tree<any>(
new TreeNode<number>(1, [new TreeNode<number>(2, [new TreeNode<number>(4, [])])]));
expect(tree.contains(subtree1)).toEqual(true);
expect(tree.contains(subtree2)).toEqual(true);
expect(tree.contains(subtree3)).toEqual(true);
expect(tree.contains(notSubtree1)).toEqual(false);
expect(tree.contains(notSubtree2)).toEqual(false);
});
});
});

View File

@ -14,6 +14,7 @@
},
"files": [
"src/router.ts",
"test/tree.spec.ts",
"test/router.spec.ts",
"typings/main.d.ts"
]