UNPKG

@n1ru4l/toposort

Version:

toposort using modern ecma script data structures

73 lines (71 loc) 2.22 kB
const toposort = (dag) => { const inDegrees = countInDegrees(dag); let { roots, nonRoots } = getRootsAndNonRoots(inDegrees); const sorted = []; while (roots.size) { sorted.push(roots); const newRoots = new Set(); for (const root of roots) { for (const dependent of dag.get(root)) { inDegrees.set(dependent, inDegrees.get(dependent) - 1); if (inDegrees.get(dependent) === 0) { newRoots.add(dependent); } } } roots = newRoots; } nonRoots = getRootsAndNonRoots(inDegrees).nonRoots; if (nonRoots.size) { throw Error("Cycle(s) detected; toposort only works on acyclic graphs"); } return sorted; }; const toposortReverse = (deps) => { const dag = reverse(deps); return toposort(dag); }; const countInDegrees = (dag) => { var _a, _b; const counts = new Map(); for (const [vx, dependents] of dag.entries()) { counts.set(vx, (_a = counts.get(vx)) !== null && _a !== void 0 ? _a : 0); for (const dependent of dependents) { counts.set(dependent, ((_b = counts.get(dependent)) !== null && _b !== void 0 ? _b : 0) + 1); } } return counts; }; const getRootsAndNonRoots = (counts) => { const roots = new Set(); const nonRoots = new Set(); for (const [id, deg] of counts.entries()) { if (deg === 0) { roots.add(id); } else if (deg !== 0) { nonRoots.add(id); } } return { roots, nonRoots }; }; const reverse = (deps) => { const reversedDeps = new Map(); for (const [name, dependsOn] of deps.entries()) { let set = reversedDeps.get(name); if (set === undefined) { set = new Set(); reversedDeps.set(name, set); } for (const dependsOnName of dependsOn) { let set = reversedDeps.get(dependsOnName); if (set === undefined) { set = new Set(); reversedDeps.set(dependsOnName, set); } set.add(name); } } return reversedDeps; }; export { toposort, toposortReverse };