| | |
| | | } |
| | | } |
| | | |
| | | // Remove node and all edges connected to it |
| | | removeNode(node: T): void { |
| | | if (this._graph.has(node)) { |
| | | // first remove all edges so other nodes don't have references to this node |
| | | for (const target of this._graph.get(node)!.outgoing) { |
| | | this.removeEdge(node, target) |
| | | } |
| | | for (const source of this._graph.get(node)!.incoming) { |
| | | this.removeEdge(source, node) |
| | | } |
| | | this._graph.delete(node) |
| | | } |
| | | } |
| | | |
| | | forEachNode(callback: (node: T) => void): void { |
| | | for (const node of this._graph.keys()) { |
| | | callback(node) |
| | | } |
| | | } |
| | | |
| | | hasEdge(from: T, to: T): boolean { |
| | | return Boolean(this._graph.get(from)?.outgoing.has(to)) |
| | | } |
| | |
| | | |
| | | // DEPENDENCY ALGORITHMS |
| | | |
| | | // Add all nodes and edges from other graph to this graph |
| | | mergeGraph(other: DepGraph<T>): void { |
| | | other.forEachEdge(([source, target]) => { |
| | | this.addNode(source) |
| | | this.addNode(target) |
| | | this.addEdge(source, target) |
| | | }) |
| | | } |
| | | |
| | | // For the node provided: |
| | | // If node does not exist, add it |
| | | // If an incoming edge was added in other, it is added in this graph |
| | |
| | | }) |
| | | } |
| | | |
| | | // Remove all nodes that do not have any incoming or outgoing edges |
| | | // A node may be orphaned if the only node pointing to it was removed |
| | | removeOrphanNodes(): Set<T> { |
| | | let orphanNodes = new Set<T>() |
| | | |
| | | this.forEachNode((node) => { |
| | | if (this.inDegree(node) === 0 && this.outDegree(node) === 0) { |
| | | orphanNodes.add(node) |
| | | } |
| | | }) |
| | | |
| | | orphanNodes.forEach((node) => { |
| | | this.removeNode(node) |
| | | }) |
| | | |
| | | return orphanNodes |
| | | } |
| | | |
| | | // Get all leaf nodes (i.e. destination paths) reachable from the node provided |
| | | // Eg. if the graph is A -> B -> C |
| | | // D ---^ |