@n1ru4l/toposort
Version:
toposort using modern ecma script data structures
78 lines (74 loc) • 2.33 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
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;
};
exports.toposort = toposort;
exports.toposortReverse = toposortReverse;