graph-curry
Version:
a Map based implementation of canonical graph data algorithms
61 lines (50 loc) • 2.24 kB
JavaScript
import { addBinSet, asSet, get, hasK, lastK, mapDiff, popFirst, spread, spreadK, } from 'fenugreek-collections';
import { initPath, nextPath, } from './path';
import { components, } from './components';
// **dfs** `:: Map<edge> -> node -> Map<pathEntry>`
// depth first traversal
export const dfs = edges => (src) => {
// >**dfs.trav** `:: Map<pathEntry> -> [node, w] -> Map<pathEntry>`
// >depth first traversal
const trav = (path = initPath(src), [ n, w ] = [ lastK(path), 0 ]) =>
spread(mapDiff(edges.get(n))(path)).reduce(trav, nextPath(path, [ n, w ]));
return trav(initPath(src));
};
// **bfs** `:: Map<edge> -> node -> Map<pathEntry>`
// breadth first traversal
export const bfs = edges => (iNode) => {
const bVisit = bPath => (bQueue) => {
const pred = popFirst(bQueue);
const nextNabes = mapDiff(edges.get(pred))(bPath);
spread(nextNabes).reduce(nextPath, bPath);
spreadK(nextNabes).reduce(addBinSet, bQueue);
return bQueue.size > 0 ? bVisit(bPath)(bQueue) : bPath;
};
return bVisit(initPath(iNode))(asSet([ iNode ]));
};
// **dijkstra** `:: Map<edge> -> node -> Map<pathEntry>`
// finds shortest paths from a source node to all node reachable from that node
export const dijkstra = edges => (iNode) => {
const reachables = bfs(edges)(iNode);
const inspectQueue = asSet([ iNode ]);
const solutionSet = initPath(iNode);
while (inspectQueue.size > 0) {
const pred = popFirst(inspectQueue);
const nextNabes = edges.get(pred);
const { length: dCount, weight: dWeight } = solutionSet.get(pred);
for (const [ nabe, nWeight ] of nextNabes) {
const prevMap = reachables.get(nabe) || { length: 1, weight: 0 };
const { length: rCount, weight: rWeight } = prevMap;
const dMap = { pred, length: dCount + 1, weight: dWeight + nWeight, };
const sMap = ((dWeight + nWeight) < rWeight) ? dMap : prevMap;
if (!solutionSet.has(nabe)) {
inspectQueue.add(nabe);
solutionSet.set(nabe, sMap);
}
}
}
return solutionSet;
};
// **pathBetween** `:: Map<edge> -> node -> node -> Boolean`
// checks for a path between two nodes
export const pathBetween = e => n0 => n1 => hasK(get(components(e))(n0))(n1);