feat: implement Tree
This commit is contained in:
parent
01111a1122
commit
37c5320e33
|
@ -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>[]) {}
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -14,6 +14,7 @@
|
|||
},
|
||||
"files": [
|
||||
"src/router.ts",
|
||||
"test/tree.spec.ts",
|
||||
"test/router.spec.ts",
|
||||
"typings/main.d.ts"
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue