-
-
Notifications
You must be signed in to change notification settings - Fork 143
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- add topoSort() as lightweight alt for thi.ng/dgraph - add tests - update readme
- Loading branch information
1 parent
b27fd07
commit f7f2e20
Showing
6 changed files
with
90 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import type { Fn, Nullable, IObjectOf } from "@thi.ng/api"; | ||
import { illegalState } from "@thi.ng/errors"; | ||
|
||
/** | ||
* Takes an object describing a DAG of nodes of type T with keys being node IDs. | ||
* Also takes a function which will be applied to each node and either returns | ||
* an array of node IDs the given node depends on or null if the node has no | ||
* dependencies. Traverses all nodes in the graph (object) and returns an array | ||
* of node IDs in topological dependency order. | ||
* | ||
* @remarks | ||
* An error will be thrown if the graph contains cycles. In this case, the full | ||
* cycle will be part of the error message (see second example below). | ||
* | ||
* @example | ||
* ```ts | ||
* const graph = { | ||
* a: { deps: ["c", "b"] }, | ||
* b: {}, | ||
* c: { deps: ["d"] }, | ||
* d: { deps: ["b"] } | ||
* }; | ||
* topoSort(graph, (node) => node.deps); | ||
* // [ "b", "d", "c", "a" ] | ||
* | ||
* // An error will be thrown if the graph contains cycles... | ||
* graph.d.deps.push("a"); | ||
* | ||
* topoSort(graph, (node) => node.deps); | ||
* // Uncaught Error: illegal state: dependency cycle: a -> c -> d -> a | ||
* ``` | ||
* | ||
* @param nodes | ||
* @param deps | ||
* @returns | ||
*/ | ||
export const topoSort = <T>( | ||
nodes: IObjectOf<T>, | ||
deps: Fn<T, Nullable<string[]>> | ||
) => { | ||
const cycles: IObjectOf<boolean> = {}; | ||
const topology: string[] = []; | ||
const sort = (id: string, path: string[]) => { | ||
if (cycles[id]) illegalState(`dependency cycle: ${path.join(" -> ")}`); | ||
cycles[id] = true; | ||
const nodeDeps = deps(nodes[id]); | ||
if (nodeDeps) { | ||
for (let d of nodeDeps) sort(d, [...path, d]); | ||
} | ||
cycles[id] = false; | ||
if (!topology.includes(id)) topology.push(id); | ||
}; | ||
for (let id in nodes) sort(id, [id]); | ||
return topology; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import * as assert from "assert"; | ||
import { group } from "@thi.ng/testament"; | ||
import { topoSort } from "../src/index.js"; | ||
|
||
const graph: Record<string, { deps?: string[] }> = { | ||
a: { deps: ["c", "b"] }, | ||
b: {}, | ||
c: { deps: ["d"] }, | ||
d: { deps: ["b"] }, | ||
}; | ||
|
||
group("topoSort", { | ||
dag: () => { | ||
assert.deepStrictEqual( | ||
topoSort(graph, (x) => x.deps), | ||
["b", "d", "c", "a"] | ||
); | ||
}, | ||
"cycle detection": () => { | ||
assert.throws(() => | ||
topoSort({ ...graph, d: { deps: ["b", "a"] } }, (x) => x.deps) | ||
); | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters