Graph.js

  1. import { _ } from '@kitmi/utils';
  2. import TopoSort from './TopoSort';
  3. class Graph {
  4. /**
  5. * Directed graph.
  6. * @constructs Graph
  7. * @param {object} json
  8. * @property {object} json.nodes - key-value pairs of nodes
  9. * @property {object} json.edges - key-value pairs of edges, source => [target1, target2, ...]
  10. */
  11. constructor(json) {
  12. this.topo = new TopoSort();
  13. if (json) {
  14. this.nodes = _.cloneDeep(json.nodes);
  15. if (!_.isEmpty(json.edges)) {
  16. _.forOwn(json.edges, (targets, source) => {
  17. this.topo.add(source, targets);
  18. });
  19. }
  20. this.startNodes = json.startNodes;
  21. this.endNodes = json.endNodes;
  22. } else {
  23. this.nodes = {};
  24. }
  25. }
  26. hasNode(key) {
  27. return key in this.nodes;
  28. }
  29. getNode(key) {
  30. return this.nodes[key];
  31. }
  32. setNode(key, value) {
  33. this.nodes[key] = value;
  34. return this;
  35. }
  36. setEdge(sourceNode, targetNode) {
  37. if (!this.hasNode(sourceNode)) {
  38. throw new Error(`Source node [${sourceNode}] not exists.`);
  39. }
  40. if (!this.hasNode(targetNode)) {
  41. throw new Error(`Target node [${targetNode}] not exists.`);
  42. }
  43. this.topo.add(sourceNode, targetNode);
  44. return this;
  45. }
  46. getTargetNodes(sourceNode) {
  47. return Array.from(this.topo.mapOfDependents[sourceNode]);
  48. }
  49. getSourceNodes(targetNode) {
  50. return Array.from(this.topo.mapOfDependencies[targetNode]);
  51. }
  52. /**
  53. * Calculate start and end nodes.
  54. * @returns {Graph}
  55. */
  56. calcStartEnd() {
  57. const seq = this.topo.sort();
  58. this.startNodes = _.takeWhile(seq, (e) => !this.topo.hasDependency(e));
  59. this.endNodes = _.takeRightWhile(seq, (e) => !this.topo.hasDependent(e));
  60. if (this.startNodes.length === 0) {
  61. this.startNodes = Object.keys(this.nodes);
  62. }
  63. if (this.endNodes.length === 0) {
  64. this.endNodes = Object.keys(this.nodes);
  65. }
  66. return this;
  67. }
  68. toJSON() {
  69. return {
  70. nodes: this.nodes,
  71. edges: _.mapValues(this.topo.mapOfDependents, (nodes) => Array.from(nodes)),
  72. startNodes: this.startNodes,
  73. endNodes: this.endNodes,
  74. };
  75. }
  76. }
  77. export default Graph;