Utilities for working with simple tree representation.
Use the package manager pnpm to install @tabula/tree-utils
.
pnpm add @tabula/tree-utils
This package works with trees, which defined in the simple format.
type Leaf = {
id: number | string;
};
type Branch = {
id: number | string;
children: Leaf[];
};
type Node = Leaf | Branch;
type Tree = Node[];
Of course, real types allows to extend properties of nodes.
We use the following tree for examples:
const tree = [
{ id: '1' },
{
id: '2',
children: [
{ id: '2-1' },
{
id: '2-2',
children: [
{ id: '2-2-1' },
{
id: '2-2-2',
children: [{ id: '2-2-2-1' }, { id: '2-2-2-2' }],
},
{ id: '2-2-3' },
],
},
],
},
{ id: '3' },
{
id: '4',
children: [
{ id: '4-1' },
{ id: '4-2' },
{
id: '4-3',
children: [{ id: '4-3-1' }, { id: '4-3-2' }],
},
],
},
];
There are breadth
and depth
functions available to traverse by the tree in iterator style. Each of them returns
traverse item on each step, which can be specific for leaf or branch.
Each item has the following properties:
-
node
: is node object itself; -
isBranch
: boolean value whichtrue
if node is branch; -
isLeaf
: boolean value whichtrue
if node is leaf; -
level
: nesting level starting from0
; -
parentId
: optional id of direct parent node; -
parentIds
: set of all parent ids from each nesting level.
We mark a node as branch if node has children
property (even if it's an empty array and hasn't any children).
This method returns iterator for traverse by tree using breadth-first search.
import { breadth } from "@tabula/tree-utils";
for (const { node } of breadth(tree)) {
console.log(node.id);
}
// 1, 2, 3, 4, 2-1, 2-2, 4-1, 4-2, 4-3, 2-2-1, 2-2-2, 2-2-3, 4-3-1, 4-3-2, 2-2-2-1, 2-2-2-2
You can pass filter for nodes.
import { breadth } from "@tabula/tree-utils";
const filter = (item) => item.level < 3 && item.node.id !== '4';
for (const { node } of breadth(tree, { filter })) {
console.log(node.id);
}
// 1, 2, 3, 2-1, 2-2, 2-2-1, 2-2-2, 2-2-3
You can pass id for subtree, to iterate over that subtree.
import { breadth } from "@tabula/tree-utils";
for (const { node } of breadth(tree, { subTree: '2-2' })) {
console.log(node.id);
}
// 2-2, 2-2-1, 2-2-2, 2-2-3, 2-2-2-1, 2-2-2-2
You can combine the subTree
and filter
options. There are items of subtree will be filtered.
import { breadth } from "@tabula/tree-utils";
const filter = (item) => item.level < 3;
for (const { node } of breadth(tree, { filter, subTree: '2-2' })) {
console.log(node.id);
}
// 2-2, 2-2-1, 2-2-2, 2-2-3
This method returns iterator for traverse by tree using depth-first search.
import { depth } from "@tabula/tree-utils";
for (const { node } of depth(tree)) {
console.log(node.id);
}
// 1, 2, 2-1, 2-2, 2-2-1, 2-2-2, 2-2-2-1, 2-2-2-2, 2-2-3, 3, 4, 4-1, 4-2, 4-3, 4-3-1, 4-3-2
You can pass filter for nodes.
import { depth } from "@tabula/tree-utils";
const filter = (item) => item.level < 3 && item.node.id !== '4';
for (const { node } of depth(tree, { filter })) {
console.log(node.id);
}
// 1, 2, 2-1, 2-2, 2-2-1, 2-2-2, 2-2-3, 3
You can pass id for subtree, to iterate over that subtree.
import { depth } from "@tabula/tree-utils";
for (const { node } of depth(tree, { subTree: '2-2' })) {
console.log(node.id);
}
// 2-2, 2-2-1, 2-2-2, 2-2-2-1, 2-2-2-2, 2-2-3
You can combine the subTree
and filter
options. There are items of subtree will be filtered.
import { depth } from "@tabula/tree-utils";
const filter = (item) => item.level < 3;
for (const { node } of depth(tree, { filter, subTree: '2-2' })) {
console.log(node.id);
}
// 2-2, 2-2-1, 2-2-2, 2-2-3
This project is ISC licensed.