UNPKG

pertain

Version:

Automated pub/sub across project dependencies. Run code from any installed package based on declarative rules in package.json

65 lines (55 loc) 1.81 kB
import PertainError from './PertainError'; /* eslint-disable */ /** * Topologically sort a list of items of any type, given a list of items and * a map of outward relations between those items. (Could be a function, but * it's a class for testability.) * * Credit to https://github.com/marcelklehr/toposort/ for a great * implementation I had to adapt to be faster for our particular use case. * * That code appears here courtesy of the MIT license. */ export default class TopologicalSorter<T> { private outgoingFrom: (node: T) => T[]; constructor(outgoingFrom: (node: T) => T[]) { this.outgoingFrom = outgoingFrom; } public sort(nodes: T[]): T[] { let cursor = nodes.length; const sorted: T[] = new Array(cursor); const visited: { [key: number]: boolean } = {}; let i = cursor; // Better data structures make algorithm much faster. const nodesHash = this.makeNodesHash(nodes); const { outgoingFrom } = this; while (i--) { if (!visited[i]) visit(nodes[i], i, new Set()); } return sorted; function visit(node: T, i: number, predecessors: Set<T>): void { if (predecessors.has(node)) { throw new PertainError(`Cyclic dependency on ${node}`); } if (visited[i]) return; visited[i] = true; const outgoing = outgoingFrom(node); if ((i = outgoing.length)) { predecessors.add(node); do { let child = outgoing[--i]; visit(child, nodesHash.get(child) as number, predecessors); } while (i); predecessors.delete(node); } sorted[--cursor] = node; } } private makeNodesHash(arr: T[]) { const res = new Map<T, number>(); for (let i = 0, len = arr.length; i < len; i++) { res.set(arr[i], i); } return res; } }