@loken/hierarchies
Version:
Library for working with hierarchies of identifiers and identifiable objects.
74 lines (58 loc) • 1.71 kB
text/typescript
import { LinearQueue, LinearStack, type Some } from '@loken/utilities';
import { GraphSignal } from './graph-signal.js';
import { type GraphTraversal, type NextNodes, type SignalNodes, type TraversalType } from './graph.types.js';
/**
* Generate a sequence of nodes by traversing the provided `roots` according to the options.
*/
export const traverseGraph = <TNode>(options: GraphTraversal<TNode>): Generator<TNode, void, undefined> => {
if (options.signal !== undefined)
return traverseSignalGraph(options);
else
return traverseFullGraph(options);
};
/** @internalexport */
export function* traverseSignalGraph<TNode>(
options: {
roots: Some<TNode>,
signal: SignalNodes<TNode>,
type?: TraversalType
detectCycles?: boolean,
},
) {
const signal = new GraphSignal<TNode>(options);
const signalFn = options.signal;
let res = signal.tryGetNext();
while (res[1]) {
signalFn(res[0], signal);
if (signal.shouldYield())
yield res[0];
signal.cleanup();
res = signal.tryGetNext();
}
}
/** @internalexport */
export function* traverseFullGraph<TNode>(
options: {
roots: Some<TNode>,
next: NextNodes<TNode>,
type?: TraversalType,
detectCycles?: boolean,
},
) {
const visited = options.detectCycles ? new Set<TNode>() : undefined;
const store = options.type === 'depth-first'
? new LinearStack<TNode>()
: new LinearQueue<TNode>();
store.attach(options.roots);
const nextFn = options.next;
while (store.count > 0) {
const node = store.detach()!;
if (visited?.has(node))
continue;
visited?.add(node);
yield node;
const children = nextFn(node);
if (children)
store.attach(children);
}
}