@domeadev/tree-helpers
v0.1.2
Published
A collection of functions for managing tree structures
Maintainers
Readme
Tree Helpers
A collection of TypeScript utility functions for managing tree data structures, focusing on operations like navigation, manipulation, and state management of hierarchical data.
Installation
npm
npm install @domeadev/tree-helpersYarn
yarn add @domeadev/tree-helperspnpm
pnpm add @domeadev/tree-helpersCore Concepts
Tree Helpers works with these basic concepts:
- Nodes: Any object with a unique key identifier
- Trees: Hierarchical structures where nodes can have child nodes
- CheckedState: For checkable trees, a collection of keys representing checked nodes
API Reference
Tree Navigation
walkNodes(nodes, getChildren, fn)
Walks through all nodes in a tree, executing a callback function on each one.
walkNodes<T>(
nodes: T[],
getChildren: (node: T) => T[] | undefined,
fn: (node: T, children: T[] | undefined) => void
): voidflattenNodes(nodes, getChildren)
Flattens a tree structure into a single-level collection.
flattenNodes<T>(
nodes: T[],
getChildren: (node: T) => T[]
): Set<T>getAncestorKeys(keyToChildKeysMap, key)
Gets all ancestor keys for a given node key.
getAncestorKeys(
keyToChildKeysMap: KeyToChildKeysMap,
key: NodeKey
): NodeKey[]Tree Construction
makeNodesMap(nodes, getKey, getChildren)
Creates a map of nodes indexed by their keys.
makeNodesMap<T>(
nodes: T[],
getKey: (node: T) => NodeKey,
getChildren: (node: T) => T[]
): Map<NodeKey, T>makeKeyToChildKeysMap(tree, getKey, getChildren)
Creates a map that relates each node key to its child keys.
makeKeyToChildKeysMap<T>(
tree: T[],
getKey: (n: T) => NodeKey,
getChildren: (n: T) => T[] | undefined
): KeyToChildKeysMapmakeTree(rows, getChildren, transformNode)
Recursively transforms rows into a tree structure with custom node transformation.
makeTree<R extends object, T>(
rows: R[],
getChildren: (row: R) => R[] | undefined,
transformNode: (row: R, childNodes: T[], children?: R[]) => T | null
): T[]makeRowsTree({ rows, getKey, getChildren, rootKeys, childrenKey })
Transforms a flat array of rows into a hierarchical tree structure.
makeRowsTree<T extends object, ChildrenKey extends string>({
rows,
getKey,
getChildren,
rootKeys,
childrenKey,
}: {
rows: T[];
getKey: (node: T) => NodeKey;
getChildren: (node: T) => T[];
rootKeys: NodeKey[];
childrenKey: ChildrenKey;
}): TreeNode<T, ChildrenKey>[]Tree Manipulation
removeNodeKey(keyToChildKeysMap, keyToRemove)
Removes a node and all its descendants from the key-to-child-keys map.
removeNodeKey(
keyToChildKeysMap: KeyToChildKeysMap,
keyToRemove: NodeKey
): KeyToChildKeysMapCheckable Tree Operations
toggleNodeCheckedState(keyToChildKeysMap, checkedState, currentNode)
Toggles a node's checked state and updates its children and ancestors accordingly.
toggleNodeCheckedState(
keyToChildKeysMap: KeyToChildKeysMap,
checkedState: CheckedState,
currentNode: CheckableNode
): Set<NodeKey>autoUncheckParentNode(keyToChildKeysMap, unCheckedKey, checkedState)
Automatically unchecks a parent node when all its children are unchecked.
autoUncheckParentNode(
keyToChildKeysMap: KeyToChildKeysMap,
unCheckedKey: NodeKey,
checkedState: Set<NodeKey>
): Set<NodeKey>Types
// Unique identifier for a node
type NodeKey = string | number;
// Map relating each node key to its child keys
type KeyToChildKeysMap = Record<NodeKey, NodeKey[]>;
// Node with a checkable state
interface CheckableNode {
key: NodeKey;
checked: boolean;
}
// Collection of checked node keys
type CheckedState = Set<NodeKey> | NodeKey[];
// Generic tree node structure
type TreeNode<T extends object, ChildrenKey extends string> = T &
Record<ChildrenKey, TreeNode<T, ChildrenKey>[]>;Examples
Building a custom tree structure
import { makeTree } from '@domeadev/tree-helpers';
interface Row {
id: number;
name: string;
parentId: number | null;
}
interface TreeNode {
id: number;
name: string;
children: TreeNode[];
}
const rows: Row[] = [
{ id: 1, name: 'Root', parentId: null },
{ id: 2, name: 'Branch A', parentId: 1 },
{ id: 3, name: 'Branch B', parentId: 1 },
{ id: 4, name: 'Leaf A1', parentId: 2 },
{ id: 5, name: 'Leaf A2', parentId: 2 },
];
const tree = makeTree(
rows.filter(row => row.parentId === null), // Start with root nodes
(row) => rows.filter(child => child.parentId === row.id), // Get children
(row, childNodes) => ({
id: row.id,
name: row.name,
children: childNodes
})
);
// Result:
// [
// {
// id: 1,
// name: 'Root',
// children: [
// {
// id: 2,
// name: 'Branch A',
// children: [
// { id: 4, name: 'Leaf A1', children: [] },
// { id: 5, name: 'Leaf A2', children: [] }
// ]
// },
// {
// id: 3,
// name: 'Branch B',
// children: []
// }
// ]
// }
// ]Creating a tree from flat data
import { makeRowsTree } from "@domeadev/tree-helpers";
const rows = [
{ id: 1, name: "Parent", parentId: null },
{ id: 2, name: "Child 1", parentId: 1 },
{ id: 3, name: "Child 2", parentId: 1 },
{ id: 4, name: "Grandchild", parentId: 2 },
];
const tree = makeRowsTree({
rows,
getKey: (row) => row.id,
getChildren: (row) => rows.filter((child) => child.parentId === row.id),
rootKeys: [1],
childrenKey: "children",
});
// Result:
// [
// {
// id: 1,
// name: 'Parent',
// parentId: null,
// children: [
// {
// id: 2,
// name: 'Child 1',
// parentId: 1,
// children: [
// {
// id: 4,
// name: 'Grandchild',
// parentId: 2,
// children: []
// }
// ]
// },
// {
// id: 3,
// name: 'Child 2',
// parentId: 1,
// children: []
// }
// ]
// }
// ]Managing checked states in a checkable tree
import {
toggleNodeCheckedState,
makeKeyToChildKeysMap,
} from "@domeadev/tree-helpers";
const treeData = [
{
key: "parent",
label: "Parent",
children: [
{ key: "child1", label: "Child 1" },
{ key: "child2", label: "Child 2" },
],
},
];
// Create a map of parent-child relationships
const keyToChildKeysMap = makeKeyToChildKeysMap(
treeData,
(node) => node.key,
(node) => node.children
);
// Initial checked state
let checkedState = new Set(["child1"]);
// Toggle the 'parent' node to checked
const parentNode = { key: "parent", checked: true };
checkedState = toggleNodeCheckedState(
keyToChildKeysMap,
checkedState,
parentNode
);
// Result: Set { 'child1', 'parent', 'child2' }
// When a parent is checked, all children are automatically checkedLicense
MIT © domeafavour
