@rimbu/graph
Version:
Immutable Graph data structures for TypeScript
107 lines • 4.33 kB
JavaScript
import { OptLazy } from '@rimbu/common';
import { HashSet } from '@rimbu/hashed';
import { SortedSet } from '@rimbu/sorted';
import { Stream } from '@rimbu/stream';
import { FastIteratorBase, StreamBase } from '@rimbu/stream/custom';
class GraphDepthFirstStream extends StreamBase {
constructor(node, graph, addVisitedNode) {
super();
this.node = node;
this.graph = graph;
this.addVisitedNode = addVisitedNode;
}
[Symbol.iterator]() {
return new GraphDepthFirstIterable(this.node, this.graph, this.addVisitedNode);
}
}
class GraphDepthFirstIterable extends FastIteratorBase {
constructor(node, graph, addVisitedNode, isRoot = true) {
super();
this.node = node;
this.graph = graph;
this.addVisitedNode = addVisitedNode;
if (isRoot)
this.addVisitedNode(node);
const startConnectionStream = this.graph.getConnectionStreamFrom(this.node);
this.arrowIterator = startConnectionStream[Symbol.iterator]();
}
fastNext(otherwise) {
if (this.currentIterator) {
const nextNode = this.currentIterator.fastNext();
if (undefined !== nextNode)
return nextNode;
}
let nextConnection;
while (undefined !== (nextConnection = this.arrowIterator.fastNext())) {
const result = nextConnection;
const targetNode = result[1];
if (this.addVisitedNode(targetNode)) {
this.currentIterator = new GraphDepthFirstIterable(targetNode, this.graph, this.addVisitedNode, false);
return result;
}
}
return OptLazy(otherwise);
}
}
/**
* Returns a stream of connections that can be reached in the given `graph`
* starting at the given `startNode`, and using depth-first traversal. It can
* avoid loops if needed in a custom way by supplying the `addVisitedNode` function.
* @param graph - the graph to traverse
* @param startNode - the start node within the graph
* @param addVisitedNode - a function taking the currenty traversed node,
* and returning true if the node has been traversed before, or false otherwise
* @example
* ```ts
* const g = EdgeGraphHashed.of([1, 2], [2, 3], [1, 3], [3, 4])
* const stream = traverseDepthFirstCustom(g, 1)
* console.log(stream.toArray())
* // => [[1, 2], [2, 3], [1, 3], [3, 4]]
* ```
*/
export function traverseDepthFirstCustom(graph, startNode, addVisitedNode = () => true) {
if (!graph.nonEmpty() || !graph.hasNode(startNode))
return Stream.empty();
return new GraphDepthFirstStream(startNode, graph, addVisitedNode);
}
/**
* Returns a stream of connections that can be reached in the given `graph`
* starting at the given `startNode`, and using depth-first traversal. It avoids
* loops by internally placing the visited nodes in a HashSet builder.
* @param graph - the graph to traverse
* @param startNode - the start node within the graph
* @example
* ```ts
* const g = EdgeGraphHashed.of([1, 2], [2, 3], [1, 3], [3, 4])
* const stream = traverseDepthFirstHashed(g, 1)
* console.log(stream.toArray())
* // => [[1, 2], [2, 3], [1, 3], [3, 4]]
* ```
*/
export function traverseDepthFirstHashed(graph, startNode) {
if (!graph.nonEmpty() || !graph.hasNode(startNode))
return Stream.empty();
const visitSet = HashSet.builder();
return new GraphDepthFirstStream(startNode, graph, visitSet.add);
}
/**
* Returns a stream of connections that can be reached in the given `graph`
* starting at the given `startNode`, and using depth-first traversal. It avoids
* loops by internally placing the visited nodes in a SortedSet builder.
* @param graph - the graph to traverse
* @param startNode - the start node within the graph
* @example
* ```ts
* const g = EdgeGraphHashed.of([1, 2], [2, 3], [1, 3], [3, 4])
* const stream = traverseDepthFirstSorted(g, 1)
* console.log(stream.toArray())
* // => [[1, 2], [2, 3], [1, 3], [3, 4]]
* ```
*/
export function traverseDepthFirstSorted(graph, startNode) {
if (!graph.nonEmpty() || !graph.hasNode(startNode))
return Stream.empty();
const visitSet = SortedSet.builder();
return new GraphDepthFirstStream(startNode, graph, visitSet.add);
}
//# sourceMappingURL=traverse-depth-first.mjs.map