Tree in Javascript (and Typescript).
Please read the wikipedia article on trees - computer science.
npm i @jlguenego/tree
This library exposes both:
- ES2015 module that can be tree-shaked by Webpack for Angular etc.
- CommonJS module for traditionnal node way.
It is ready to use for both browsers and node app.
Sometimes, the simplest way to represent a tree is to give an object where each property key represents each node, and the corresponding value is its children.
const adjList: AdjacencyList = {
1: ['2', '3', '4'],
2: ['5', '6'],
6: ['7', '8'],
};
const tree = Tree.fromAdjencyList(adjList);
console.log('tree: ', tree);
If the node are not string, but more complex object, we can do like this:
const adjList: AdjacencyList = {
1: ['2', '3', '4'],
2: ['5', '6'],
6: ['7', '8'],
};
const nodeMap = ([
'',
'lorem',
'ipsum',
'dolor',
'sit',
'amet',
'consectetur',
'adipiscing',
'elit',
] as unknown) as NodeMap<string>;
const tree = Tree.fromAdjacencyListAndNodeMap(adjList, nodeMap);
console.log('tree: ', tree);
const expectedTree = Tree.fromObject({
node: '1',
children: [
{
node: '2',
children: [
{node: '5'},
{node: '6', children: [{node: '7'}, {node: '8'}]},
],
},
{node: '3'},
{node: '4'},
],
});
To build a tree with no child:
const tree = new Tree<number>(23);
To build a tree with some children :
const tree = new Tree<number>(23, [
new Tree<number>(12),
new Tree<number>(13),
new Tree<number>(7),
]);
This above example creates a tree with a root node (23) and 3 children leaf with 12, 13 and 7.
A tree is just a class instance with 2 attributes:
node
: representing the node valuechildren
: reprensenting the node children.
tree.toObject();
A tree class instance has following methods:
isBranch()
: Test if the tree has at least one child.isLeaf()
: Test if the tree has no child.flatten()
: Returns a list of all leaf node values.getLeaves()
: Returns a list of all leaf subtrees.enumerateDepthFirst()
: Returns a list of all branches and leaves values (a child is completely recursively visited before processing the other children).enumerateBreadthFirst()
: Returns a list of all branches and leaves values (all the first level children are first listed, then all the second level, and so on.).clone()
: returns a shallow clone of the tree. All the structure is duplicated but the node values are not cloned.find(cb: (subtree)=> boolean)
: Find the first subtree satisfying criteria specified bycb
.getPath(subtree): Retrieve the path of the subtree in the tree. Return an
number[]if found with the children indexes, or
undefined` if not found.getSubtree(path)
: Returns the subtree matching the path if possible. Can throw error if bad path.
graft(stock, scion, index?)
: add a subtree (scion
) to the tree (stock
). Index can optionally be specified to indicate where to add the scion between the children.
This module give tools to perform Breadth-First-Search and Depth-First-Search, in a synchronous way and asynchronous way:
BFSTree
: class for doing synchronous Breadth-First-Search.BFSTreeAsync
: class for doing asynchronous Breadth-First-Search.DFSTree
: class for doing synchronous Depth-First-Search.DFSTreeAsync
: class for doing asynchronous Depth-First-Search.
Each class has the same interface:
- Instantiate the class by giving the Tree Trunk
initialValue
, thetest
method to check if the subtree is found, and thegetChildren
function for getting (or generating) the subree children if the subtree is not the searched one. - Call the
search()
method to return the searched node value subtree if it exists orundefined
if not.
For asynchronous class, there is :
- an observable (called
subject
) that can be subscribed to get some info about the search progression. See the famous RxJS library. - the
interrupt()
method to stop searching.
The example is done with Breadth-First-Search. Just repleace BFSTree
with DFSTree
if you want Depth-First-Search.
const test = (n: number) => n > 10;
const getChildren = (n: number) => [n + 1, n + 2, n + 3];
const bfsTree = new BFSTree<number>(3, test, getChildren);
const result = bfsTree.search();
assert.deepStrictEqual(result, 11);
async function main() {
this.timeout(20000);
const test = async (n: number) => n > 30;
const getChildren = async (n: number) => {
await sleep(10);
return [n + 1, 2 * n + 1];
};
const bfsTree = new BFSTreeAsync<number>(1, test, getChildren);
bfsTree.subject.subscribe(info => {
console.log('info: ', inspect(info, false, null));
});
const result = await bfsTree.search();
assert.deepStrictEqual(result, 31);
});
}
main();
Do not hesitate to bring your contribution to this project. Fork and Pull Request are welcome.
ISC
Jean-Louis GUENEGO jlguenego@gmail.com