summaryrefslogtreecommitdiff
path: root/src/static/js/pluginfw/tsort.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/static/js/pluginfw/tsort.js')
-rw-r--r--src/static/js/pluginfw/tsort.js112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/static/js/pluginfw/tsort.js b/src/static/js/pluginfw/tsort.js
new file mode 100644
index 00000000..6591c51c
--- /dev/null
+++ b/src/static/js/pluginfw/tsort.js
@@ -0,0 +1,112 @@
+/**
+ * general topological sort
+ * from https://gist.github.com/1232505
+ * @author SHIN Suzuki (shinout310@gmail.com)
+ * @param Array<Array> edges : list of edges. each edge forms Array<ID,ID> e.g. [12 , 3]
+ *
+ * @returns Array : topological sorted list of IDs
+ **/
+
+function tsort(edges) {
+ var nodes = {}, // hash: stringified id of the node => { id: id, afters: lisf of ids }
+ sorted = [], // sorted list of IDs ( returned value )
+ visited = {}; // hash: id of already visited node => true
+
+ var Node = function(id) {
+ this.id = id;
+ this.afters = [];
+ }
+
+ // 1. build data structures
+ edges.forEach(function(v) {
+ var from = v[0], to = v[1];
+ if (!nodes[from]) nodes[from] = new Node(from);
+ if (!nodes[to]) nodes[to] = new Node(to);
+ nodes[from].afters.push(to);
+ });
+
+ // 2. topological sort
+ Object.keys(nodes).forEach(function visit(idstr, ancestors) {
+ var node = nodes[idstr],
+ id = node.id;
+
+ // if already exists, do nothing
+ if (visited[idstr]) return;
+
+ if (!Array.isArray(ancestors)) ancestors = [];
+
+ ancestors.push(id);
+
+ visited[idstr] = true;
+
+ node.afters.forEach(function(afterID) {
+ if (ancestors.indexOf(afterID) >= 0) // if already in ancestors, a closed chain exists.
+ throw new Error('closed chain : ' + afterID + ' is in ' + id);
+
+ visit(afterID.toString(), ancestors.map(function(v) { return v })); // recursive call
+ });
+
+ sorted.unshift(id);
+ });
+
+ return sorted;
+}
+
+/**
+ * TEST
+ **/
+function tsortTest() {
+
+ // example 1: success
+ var edges = [
+ [1, 2],
+ [1, 3],
+ [2, 4],
+ [3, 4]
+ ];
+
+ var sorted = tsort(edges);
+ console.log(sorted);
+
+ // example 2: failure ( A > B > C > A )
+ edges = [
+ ['A', 'B'],
+ ['B', 'C'],
+ ['C', 'A']
+ ];
+
+ try {
+ sorted = tsort(edges);
+ }
+ catch (e) {
+ console.log(e.message);
+ }
+
+ // example 3: generate random edges
+ var max = 100, iteration = 30;
+ function randomInt(max) {
+ return Math.floor(Math.random() * max) + 1;
+ }
+
+ edges = (function() {
+ var ret = [], i = 0;
+ while (i++ < iteration) ret.push( [randomInt(max), randomInt(max)] );
+ return ret;
+ })();
+
+ try {
+ sorted = tsort(edges);
+ console.log("succeeded", sorted);
+ }
+ catch (e) {
+ console.log("failed", e.message);
+ }
+
+}
+
+
+// for node.js
+if (typeof exports == 'object' && exports === this) {
+ module.exports = tsort;
+ if (process.argv[1] === __filename) tsortTest();
+}