You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
41 lines
1.1 KiB
JavaScript
41 lines
1.1 KiB
JavaScript
4 years ago
|
"use strict";
|
||
|
|
||
|
const findCycle = require("find-cycle/directed");
|
||
|
|
||
|
module.exports = function findCycleSets(nodes) {
|
||
|
let queue = nodes.slice();
|
||
|
let cycleSets = [];
|
||
|
let innocentNodes = [];
|
||
|
let lastQueueLength = queue.length;
|
||
|
|
||
|
while (queue.length > 0) {
|
||
|
let queueSet = new Set(queue);
|
||
|
|
||
|
let cycle = findCycle(queue, (node) => {
|
||
|
return node.children.filter((child) => {
|
||
|
// We want to prevent findCycle from discovering and traversing any nodes that are not in our "conflict set"; otherwise a second pass will fail, because it will rediscover already-handled nodes (that have been removed from the queue) indirectly, and endlessly loop on discovering that cycle.
|
||
|
return queueSet.has(child);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
if (cycle != null) {
|
||
|
cycleSets.push(cycle);
|
||
|
queue = queue.filter((node) => !cycle.includes(node));
|
||
|
|
||
|
if (queue.length === lastQueueLength) {
|
||
|
throw new Error(`Failed to reduce queue length; this is a bug, please report it!`);
|
||
|
} else {
|
||
|
lastQueueLength = queue.length;
|
||
|
}
|
||
|
} else {
|
||
|
innocentNodes = queue;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
cycleSets: cycleSets,
|
||
|
innocentNodes: innocentNodes
|
||
|
};
|
||
|
};
|