Comprehensive traversal, manipulation, and computation methods available on all hierarchy nodes. These methods enable powerful data processing workflows before applying layout algorithms.
Methods for visiting nodes in different orders and patterns.
Visits all nodes in breadth-first order (level by level).
/**
* Visit all nodes in breadth-first order
* @param callback - Function called for each node
* @param that - Optional this context for callback
* @returns The root node for chaining
*/
node.each(callback, that?);Usage Examples:
import { hierarchy } from "d3-hierarchy";
const root = hierarchy(data);
// Basic traversal
root.each(node => {
console.log(`${node.data.name} at depth ${node.depth}`);
});
// With index and context
root.each(function(node, index) {
console.log(`Node ${index}: ${node.data.name}`);
this.nodeCount++; // Access 'this' context
}, { nodeCount: 0 });
// Collect all nodes
const allNodes = [];
root.each(node => allNodes.push(node));Visits nodes in pre-order traversal (parent before children).
/**
* Visit nodes in pre-order (parent before children)
* @param callback - Function called for each node
* @param that - Optional this context for callback
* @returns The root node for chaining
*/
node.eachBefore(callback, that?);Usage Examples:
// Pre-order traversal - parents first
root.eachBefore(node => {
console.log(`Processing ${node.data.name} before its children`);
});
// Initialize parent properties before children
root.eachBefore(node => {
if (node.parent) {
node.parentName = node.parent.data.name;
}
});Visits nodes in post-order traversal (children before parent).
/**
* Visit nodes in post-order (children before parent)
* @param callback - Function called for each node
* @param that - Optional this context for callback
* @returns The root node for chaining
*/
node.eachAfter(callback, that?);Usage Examples:
// Post-order traversal - children first
root.eachAfter(node => {
console.log(`Processing ${node.data.name} after its children`);
});
// Aggregate values from children to parents
root.eachAfter(node => {
if (node.children) {
node.totalValue = node.children.reduce((sum, child) => sum + child.totalValue, 0);
} else {
node.totalValue = node.data.value || 0;
}
});Methods for accessing related nodes and collecting node subsets.
Returns array of all descendant nodes, including the node itself.
/**
* Returns array of all descendant nodes (including self)
* @returns Array of nodes in breadth-first order
*/
node.descendants();Usage Examples:
// Get all descendants
const allNodes = root.descendants();
console.log(`Total nodes: ${allNodes.length}`);
// Filter descendants
const leafNodes = root.descendants().filter(node => !node.children);
const branchNodes = root.descendants().filter(node => node.children);
// Process descendants by depth
const nodesByDepth = {};
root.descendants().forEach(node => {
if (!nodesByDepth[node.depth]) nodesByDepth[node.depth] = [];
nodesByDepth[node.depth].push(node);
});Returns array from the node to the root, including both endpoints.
/**
* Returns array from this node to root (including both)
* @returns Array of ancestor nodes from node to root
*/
node.ancestors();Usage Examples:
// Find path to root
const someNode = root.descendants()[5];
const pathToRoot = someNode.ancestors();
console.log("Path to root:", pathToRoot.map(n => n.data.name));
// Check if node is ancestor
function isAncestor(ancestor, descendant) {
return descendant.ancestors().includes(ancestor);
}
// Get depth of node (alternative to node.depth)
const depth = someNode.ancestors().length - 1;Returns array of all leaf nodes (nodes without children).
/**
* Returns array of all leaf nodes (nodes without children)
* @returns Array of leaf nodes
*/
node.leaves();Usage Examples:
// Get all leaves
const leaves = root.leaves();
console.log(`Number of leaves: ${leaves.length}`);
// Process only leaf data
leaves.forEach(leaf => {
console.log(`Leaf: ${leaf.data.name}, Value: ${leaf.data.value}`);
});
// Calculate leaf statistics
const leafValues = leaves.map(leaf => leaf.data.value || 0);
const totalLeafValue = leafValues.reduce((sum, val) => sum + val, 0);
const avgLeafValue = totalLeafValue / leafValues.length;Returns shortest path between two nodes.
/**
* Returns shortest path from this node to target node
* @param target - Target node to find path to
* @returns Array of nodes forming the path
*/
node.path(target);Usage Examples:
// Find path between two nodes
const nodeA = root.find(n => n.data.name === "A");
const nodeB = root.find(n => n.data.name === "B");
const path = nodeA.path(nodeB);
console.log("Path:", path.map(n => n.data.name).join(" → "));
// Calculate distance between nodes
const distance = path.length - 1;
// Find common ancestor
function commonAncestor(nodeA, nodeB) {
const pathA = nodeA.ancestors();
const pathB = nodeB.ancestors();
return pathA.find(ancestor => pathB.includes(ancestor));
}Returns array of parent-child links for drawing connections.
/**
* Returns array of parent-child links
* @returns Array of {source: parent, target: child} objects
*/
node.links();Usage Examples:
// Get all links for drawing edges
const links = root.links();
links.forEach(link => {
console.log(`${link.source.data.name} → ${link.target.data.name}`);
});
// Use with D3 line generator for tree visualization
// links.forEach(link => {
// const lineGenerator = d3.line()
// .x(d => d.x)
// .y(d => d.y);
// const pathData = lineGenerator([link.source, link.target]);
// });Methods for computing values and transforming the hierarchy.
Computes the value of each node as the sum of its descendants' values.
/**
* Compute node values by summing descendant values
* @param value - Function to extract numeric value from node data
* @returns The root node for chaining
*/
node.sum(value);Usage Examples:
// Basic sum from leaf values
const root = hierarchy(data)
.sum(d => d.value || 0);
// Each node now has a 'value' property
root.each(node => {
console.log(`${node.data.name}: ${node.value}`);
});
// Custom value computation
const root2 = hierarchy(data)
.sum(d => d.size * d.weight || 1);
// Conditional value computation
const root3 = hierarchy(data)
.sum(d => d.active ? (d.value || 1) : 0);Counts the number of descendant leaf nodes.
/**
* Count the number of descendant leaf nodes
* @returns The root node for chaining
*/
node.count();Usage Examples:
// Count leaves under each node
const root = hierarchy(data).count();
root.each(node => {
console.log(`${node.data.name} has ${node.value} leaves`);
});
// Use count for uniform node sizing
const evenTreemap = treemap()
.size([400, 300])
(hierarchy(data).count()); // Each leaf gets equal areaSorts children of each node using a comparison function.
/**
* Sort children of each node using comparison function
* @param compare - Comparison function (a, b) => number
* @returns The root node for chaining
*/
node.sort(compare);Usage Examples:
// Sort by value (descending)
const root = hierarchy(data)
.sum(d => d.value || 0)
.sort((a, b) => b.value - a.value);
// Sort by name (ascending)
const sortedRoot = hierarchy(data)
.sort((a, b) => a.data.name.localeCompare(b.data.name));
// Custom sort with multiple criteria
const complexSort = hierarchy(data)
.sum(d => d.value || 0)
.sort((a, b) => {
// First by type, then by value
if (a.data.type !== b.data.type) {
return a.data.type.localeCompare(b.data.type);
}
return b.value - a.value;
});Finds the first node that matches a predicate function.
/**
* Find the first node matching the predicate
* @param filter - Function returning true for target node
* @param that - Optional this context for filter function
* @returns First matching node or undefined
*/
node.find(filter, that?);Usage Examples:
// Find node by name
const targetNode = root.find(node => node.data.name === "target");
// Find node by property
const highValueNode = root.find(node => node.data.value > 100);
// Find with context
const finder = {
targetId: "abc123",
matches(node) { return node.data.id === this.targetId; }
};
const foundNode = root.find(finder.matches, finder);
// Find first leaf
const firstLeaf = root.find(node => !node.children);
// Find deepest node
let deepestNode = root;
root.each(node => {
if (node.depth > deepestNode.depth) {
deepestNode = node;
}
});Creates a deep copy of the hierarchy.
/**
* Create a deep copy of the hierarchy
* @returns New hierarchy with copied structure and data
*/
node.copy();Usage Examples:
// Create independent copy
const originalRoot = hierarchy(data).sum(d => d.value);
const copyRoot = originalRoot.copy();
// Apply different layouts to copies
const treeVersion = tree().size([400, 300])(copyRoot);
const treemapVersion = treemap().size([400, 300])(originalRoot.copy());
// Modify copy without affecting original
copyRoot.each(node => {
node.data = { ...node.data, processed: true };
});
// originalRoot is unchangedNodes are iterable and work with for...of loops and other iterable APIs.
/**
* Iterator implementation for breadth-first traversal
* @returns Iterator yielding nodes in breadth-first order
*/
node[Symbol.iterator]();Usage Examples:
// Use with for...of
for (const node of root) {
console.log(node.data.name);
}
// Use with array methods
const nodeNames = Array.from(root).map(node => node.data.name);
const leafCount = Array.from(root).filter(node => !node.children).length;
// Use with other iterable APIs
const nodeSet = new Set(root);
const firstFiveNodes = Array.from(root).slice(0, 5);