diff --git a/src/dagre/acyclic.js b/src/dagre/acyclic.js index 912341f..9b93fa3 100644 --- a/src/dagre/acyclic.js +++ b/src/dagre/acyclic.js @@ -1,16 +1,16 @@ +import * as _ from 'lodash-es'; import { greedyFAS } from './greedy-fas.js'; -import { uniqueId } from './util.js'; export { run, undo }; function run(g) { var fas = g.graph().acyclicer === 'greedy' ? greedyFAS(g, weightFn(g)) : dfsFAS(g); - fas.forEach((e) => { + _.forEach(fas, function (e) { var label = g.edge(e); g.removeEdge(e); label.forwardName = e.name; label.reversed = true; - g.setEdge(e.w, e.v, label, uniqueId('rev')); + g.setEdge(e.w, e.v, label, _.uniqueId('rev')); }); function weightFn(g) { @@ -31,7 +31,7 @@ function dfsFAS(g) { } visited[v] = true; stack[v] = true; - g.outEdges(v).forEach((e) => { + _.forEach(g.outEdges(v), function (e) { if (Object.prototype.hasOwnProperty.call(stack, e.w)) { fas.push(e); } else { @@ -41,12 +41,12 @@ function dfsFAS(g) { delete stack[v]; } - g.nodes().forEach(dfs); + _.forEach(g.nodes(), dfs); return fas; } function undo(g) { - g.edges().forEach((e) => { + _.forEach(g.edges(), function (e) { var label = g.edge(e); if (label.reversed) { g.removeEdge(e); diff --git a/src/dagre/add-border-segments.js b/src/dagre/add-border-segments.js index 25cc420..08c6a4e 100644 --- a/src/dagre/add-border-segments.js +++ b/src/dagre/add-border-segments.js @@ -1,3 +1,4 @@ +import * as _ from 'lodash-es'; import * as util from './util.js'; export { addBorderSegments }; @@ -7,7 +8,7 @@ function addBorderSegments(g) { var children = g.children(v); var node = g.node(v); if (children.length) { - children.forEach(dfs); + _.forEach(children, dfs); } if (Object.prototype.hasOwnProperty.call(node, 'minRank')) { @@ -20,7 +21,7 @@ function addBorderSegments(g) { } } - g.children().forEach(dfs); + _.forEach(g.children(), dfs); } function addBorderNode(g, prop, prefix, sg, sgNode, rank) { diff --git a/src/dagre/coordinate-system.js b/src/dagre/coordinate-system.js index c8e8e9b..be2e52f 100644 --- a/src/dagre/coordinate-system.js +++ b/src/dagre/coordinate-system.js @@ -1,3 +1,5 @@ +import * as _ from 'lodash-es'; + export { adjust, undo }; function adjust(g) { @@ -20,8 +22,12 @@ function undo(g) { } function swapWidthHeight(g) { - g.nodes().forEach((v) => swapWidthHeightOne(g.node(v))); - g.edges().forEach((e) => swapWidthHeightOne(g.edge(e))); + _.forEach(g.nodes(), function (v) { + swapWidthHeightOne(g.node(v)); + }); + _.forEach(g.edges(), function (e) { + swapWidthHeightOne(g.edge(e)); + }); } function swapWidthHeightOne(attrs) { @@ -31,11 +37,13 @@ function swapWidthHeightOne(attrs) { } function reverseY(g) { - g.nodes().forEach((v) => reverseYOne(g.node(v))); + _.forEach(g.nodes(), function (v) { + reverseYOne(g.node(v)); + }); - g.edges().forEach((e) => { + _.forEach(g.edges(), function (e) { var edge = g.edge(e); - edge.points.forEach(reverseYOne); + _.forEach(edge.points, reverseYOne); if (Object.prototype.hasOwnProperty.call(edge, 'y')) { reverseYOne(edge); } @@ -47,11 +55,13 @@ function reverseYOne(attrs) { } function swapXY(g) { - g.nodes().forEach((v) => swapXYOne(g.node(v))); + _.forEach(g.nodes(), function (v) { + swapXYOne(g.node(v)); + }); - g.edges().forEach((e) => { + _.forEach(g.edges(), function (e) { var edge = g.edge(e); - edge.points.forEach(swapXYOne); + _.forEach(edge.points, swapXYOne); if (Object.prototype.hasOwnProperty.call(edge, 'x')) { swapXYOne(edge); } diff --git a/src/dagre/debug.js b/src/dagre/debug.js index 12573ef..f8c9e38 100644 --- a/src/dagre/debug.js +++ b/src/dagre/debug.js @@ -1,3 +1,4 @@ +import * as _ from 'lodash-es'; import { Graph } from '../graphlib/index.js'; import * as util from './util.js'; @@ -9,22 +10,22 @@ function debugOrdering(g) { var h = new Graph({ compound: true, multigraph: true }).setGraph({}); - g.nodes().forEach((v) => { + _.forEach(g.nodes(), function (v) { h.setNode(v, { label: v }); h.setParent(v, 'layer' + g.node(v).rank); }); - g.edges().forEach((e) => h.setEdge(e.v, e.w, {}, e.name)); + _.forEach(g.edges(), function (e) { + h.setEdge(e.v, e.w, {}, e.name); + }); - layerMatrix.forEach((layer, i) => { + _.forEach(layerMatrix, function (layer, i) { var layerV = 'layer' + i; h.setNode(layerV, { rank: 'same' }); - if (layer.length > 0) { - layer.reduce((u, v) => { - h.setEdge(u, v, { style: 'invis' }); - return v; - }); - } + _.reduce(layer, function (u, v) { + h.setEdge(u, v, { style: 'invis' }); + return v; + }); }); return h; diff --git a/src/dagre/greedy-fas.js b/src/dagre/greedy-fas.js index bed30e0..5cdd295 100644 --- a/src/dagre/greedy-fas.js +++ b/src/dagre/greedy-fas.js @@ -1,4 +1,4 @@ -import { range } from './util.js'; +import * as _ from 'lodash-es'; import { Graph } from '../graphlib/index.js'; import { List } from './data/list.js'; @@ -11,7 +11,7 @@ import { List } from './data/list.js'; */ export { greedyFAS }; -var DEFAULT_WEIGHT_FN = () => 1; +var DEFAULT_WEIGHT_FN = _.constant(1); function greedyFAS(g, weightFn) { if (g.nodeCount() <= 1) { @@ -21,7 +21,11 @@ function greedyFAS(g, weightFn) { var results = doGreedyFAS(state.graph, state.buckets, state.zeroIdx); // Expand multi-edges - return results.flatMap((e) => g.outEdges(e.v, e.w)); + return _.flatten( + _.map(results, function (e) { + return g.outEdges(e.v, e.w); + }), + ); } function doGreedyFAS(g, buckets, zeroIdx) { @@ -54,7 +58,7 @@ function doGreedyFAS(g, buckets, zeroIdx) { function removeNode(g, buckets, zeroIdx, entry, collectPredecessors) { var results = collectPredecessors ? [] : undefined; - g.inEdges(entry.v).forEach((edge) => { + _.forEach(g.inEdges(entry.v), function (edge) { var weight = g.edge(edge); var uEntry = g.node(edge.v); @@ -66,7 +70,7 @@ function removeNode(g, buckets, zeroIdx, entry, collectPredecessors) { assignBucket(buckets, zeroIdx, uEntry); }); - g.outEdges(entry.v).forEach((edge) => { + _.forEach(g.outEdges(entry.v), function (edge) { var weight = g.edge(edge); var w = edge.w; var wEntry = g.node(w); @@ -84,11 +88,13 @@ function buildState(g, weightFn) { var maxIn = 0; var maxOut = 0; - g.nodes().forEach((v) => fasGraph.setNode(v, { v: v, in: 0, out: 0 })); + _.forEach(g.nodes(), function (v) { + fasGraph.setNode(v, { v: v, in: 0, out: 0 }); + }); // Aggregate weights on nodes, but also sum the weights across multi-edges // into a single edge for the fasGraph. - g.edges().forEach((e) => { + _.forEach(g.edges(), function (e) { var prevWeight = fasGraph.edge(e.v, e.w) || 0; var weight = weightFn(e); var edgeWeight = prevWeight + weight; @@ -97,10 +103,12 @@ function buildState(g, weightFn) { maxIn = Math.max(maxIn, (fasGraph.node(e.w)['in'] += weight)); }); - var buckets = range(maxOut + maxIn + 3).map(() => new List()); + var buckets = _.range(maxOut + maxIn + 3).map(function () { + return new List(); + }); var zeroIdx = maxIn + 1; - fasGraph.nodes().forEach((v) => { + _.forEach(fasGraph.nodes(), function (v) { assignBucket(buckets, zeroIdx, fasGraph.node(v)); }); diff --git a/src/dagre/layout.js b/src/dagre/layout.js index 5dcafce..ee10670 100644 --- a/src/dagre/layout.js +++ b/src/dagre/layout.js @@ -1,13 +1,14 @@ +import * as _ from 'lodash-es'; import { Graph } from '../graphlib/index.js'; -import * as acyclic from './acyclic.js'; import { addBorderSegments } from './add-border-segments.js'; import * as coordinateSystem from './coordinate-system.js'; -import * as nestingGraph from './nesting-graph.js'; +import * as acyclic from './acyclic.js'; import * as normalize from './normalize.js'; +import { rank } from './rank/index.js'; +import * as nestingGraph from './nesting-graph.js'; import { order } from './order/index.js'; import { parentDummyChains } from './parent-dummy-chains.js'; import { position } from './position/index.js'; -import { rank } from './rank/index.js'; import * as util from './util.js'; export { layout }; @@ -58,7 +59,7 @@ function runLayout(g, time) { * attributes can influence layout. */ function updateInputGraph(inputGraph, layoutGraph) { - inputGraph.nodes().forEach((v) => { + _.forEach(inputGraph.nodes(), function (v) { var inputLabel = inputGraph.node(v); var layoutLabel = layoutGraph.node(v); @@ -73,7 +74,7 @@ function updateInputGraph(inputGraph, layoutGraph) { } }); - inputGraph.edges().forEach((e) => { + _.forEach(inputGraph.edges(), function (e) { var inputLabel = inputGraph.edge(e); var layoutLabel = layoutGraph.edge(e); @@ -115,37 +116,20 @@ function buildLayoutGraph(inputGraph) { var graph = canonicalize(inputGraph.graph()); g.setGraph( - Object.assign( - {}, - graphDefaults, - selectNumberAttrs(graph, graphNumAttrs), - util.pick(graph, graphAttrs), - ), + _.merge({}, graphDefaults, selectNumberAttrs(graph, graphNumAttrs), _.pick(graph, graphAttrs)), ); - inputGraph.nodes().forEach((v) => { + _.forEach(inputGraph.nodes(), function (v) { var node = canonicalize(inputGraph.node(v)); - const newNode = selectNumberAttrs(node, nodeNumAttrs); - Object.keys(nodeDefaults).forEach((k) => { - if (newNode[k] === undefined) { - newNode[k] = nodeDefaults[k]; - } - }); - - g.setNode(v, newNode); + g.setNode(v, _.defaults(selectNumberAttrs(node, nodeNumAttrs), nodeDefaults)); g.setParent(v, inputGraph.parent(v)); }); - inputGraph.edges().forEach((e) => { + _.forEach(inputGraph.edges(), function (e) { var edge = canonicalize(inputGraph.edge(e)); g.setEdge( e, - Object.assign( - {}, - edgeDefaults, - selectNumberAttrs(edge, edgeNumAttrs), - util.pick(edge, edgeAttrs), - ), + _.merge({}, edgeDefaults, selectNumberAttrs(edge, edgeNumAttrs), _.pick(edge, edgeAttrs)), ); }); @@ -163,7 +147,7 @@ function buildLayoutGraph(inputGraph) { function makeSpaceForEdgeLabels(g) { var graph = g.graph(); graph.ranksep /= 2; - g.edges().forEach((e) => { + _.forEach(g.edges(), function (e) { var edge = g.edge(e); edge.minlen *= 2; if (edge.labelpos.toLowerCase() !== 'c') { @@ -183,7 +167,7 @@ function makeSpaceForEdgeLabels(g) { * label's position. */ function injectEdgeLabelProxies(g) { - g.edges().forEach((e) => { + _.forEach(g.edges(), function (e) { var edge = g.edge(e); if (edge.width && edge.height) { var v = g.node(e.v); @@ -196,19 +180,20 @@ function injectEdgeLabelProxies(g) { function assignRankMinMax(g) { var maxRank = 0; - g.nodes().forEach((v) => { + _.forEach(g.nodes(), function (v) { var node = g.node(v); if (node.borderTop) { node.minRank = g.node(node.borderTop).rank; node.maxRank = g.node(node.borderBottom).rank; - maxRank = Math.max(maxRank, node.maxRank); + // @ts-expect-error + maxRank = _.max(maxRank, node.maxRank); } }); g.graph().maxRank = maxRank; } function removeEdgeLabelProxies(g) { - g.nodes().forEach((v) => { + _.forEach(g.nodes(), function (v) { var node = g.node(v); if (node.dummy === 'edge-proxy') { g.edge(node.e).labelRank = node.rank; @@ -237,8 +222,10 @@ function translateGraph(g) { maxY = Math.max(maxY, y + h / 2); } - g.nodes().forEach((v) => getExtremes(g.node(v))); - g.edges().forEach((e) => { + _.forEach(g.nodes(), function (v) { + getExtremes(g.node(v)); + }); + _.forEach(g.edges(), function (e) { var edge = g.edge(e); if (Object.prototype.hasOwnProperty.call(edge, 'x')) { getExtremes(edge); @@ -248,15 +235,15 @@ function translateGraph(g) { minX -= marginX; minY -= marginY; - g.nodes().forEach((v) => { + _.forEach(g.nodes(), function (v) { var node = g.node(v); node.x -= minX; node.y -= minY; }); - g.edges().forEach((e) => { + _.forEach(g.edges(), function (e) { var edge = g.edge(e); - edge.points.forEach((p) => { + _.forEach(edge.points, function (p) { p.x -= minX; p.y -= minY; }); @@ -273,7 +260,7 @@ function translateGraph(g) { } function assignNodeIntersects(g) { - g.edges().forEach((e) => { + _.forEach(g.edges(), function (e) { var edge = g.edge(e); var nodeV = g.node(e.v); var nodeW = g.node(e.w); @@ -292,7 +279,7 @@ function assignNodeIntersects(g) { } function fixupEdgeLabelCoords(g) { - g.edges().forEach((e) => { + _.forEach(g.edges(), function (e) { var edge = g.edge(e); if (Object.prototype.hasOwnProperty.call(edge, 'x')) { if (edge.labelpos === 'l' || edge.labelpos === 'r') { @@ -311,7 +298,7 @@ function fixupEdgeLabelCoords(g) { } function reversePointsForReversedEdges(g) { - g.edges().forEach((e) => { + _.forEach(g.edges(), function (e) { var edge = g.edge(e); if (edge.reversed) { edge.points.reverse(); @@ -320,13 +307,13 @@ function reversePointsForReversedEdges(g) { } function removeBorderNodes(g) { - g.nodes().forEach((v) => { + _.forEach(g.nodes(), function (v) { if (g.children(v).length) { var node = g.node(v); var t = g.node(node.borderTop); var b = g.node(node.borderBottom); - var l = g.node(node.borderLeft[node.borderLeft.length - 1]); - var r = g.node(node.borderRight[node.borderRight.length - 1]); + var l = g.node(_.last(node.borderLeft)); + var r = g.node(_.last(node.borderRight)); node.width = Math.abs(r.x - l.x); node.height = Math.abs(b.y - t.y); @@ -335,7 +322,7 @@ function removeBorderNodes(g) { } }); - g.nodes().forEach((v) => { + _.forEach(g.nodes(), function (v) { if (g.node(v).dummy === 'border') { g.removeNode(v); } @@ -343,7 +330,7 @@ function removeBorderNodes(g) { } function removeSelfEdges(g) { - g.edges().forEach((e) => { + _.forEach(g.edges(), function (e) { if (e.v === e.w) { var node = g.node(e.v); if (!node.selfEdges) { @@ -357,12 +344,12 @@ function removeSelfEdges(g) { function insertSelfEdges(g) { var layers = util.buildLayerMatrix(g); - layers.forEach((layer) => { + _.forEach(layers, function (layer) { var orderShift = 0; - layer.forEach((v, i) => { + _.forEach(layer, function (v, i) { var node = g.node(v); node.order = i + orderShift; - (node.selfEdges || []).forEach((selfEdge) => { + _.forEach(node.selfEdges, function (selfEdge) { util.addDummyNode( g, 'selfedge', @@ -383,7 +370,7 @@ function insertSelfEdges(g) { } function positionSelfEdges(g) { - g.nodes().forEach((v) => { + _.forEach(g.nodes(), function (v) { var node = g.node(v); if (node.dummy === 'selfedge') { var selfNode = g.node(node.e.v); @@ -407,19 +394,13 @@ function positionSelfEdges(g) { } function selectNumberAttrs(obj, attrs) { - return util.mapValues(util.pick(obj, attrs), Number); + return _.mapValues(_.pick(obj, attrs), Number); } function canonicalize(attrs) { var newAttrs = {}; - if (attrs) { - Object.entries(attrs).forEach(([k, v]) => { - if (typeof k === 'string') { - k = k.toLowerCase(); - } - - newAttrs[k] = v; - }); - } + _.forEach(attrs, function (v, k) { + newAttrs[k.toLowerCase()] = v; + }); return newAttrs; } diff --git a/src/dagre/nesting-graph.js b/src/dagre/nesting-graph.js index 2d3ed15..c346bb6 100644 --- a/src/dagre/nesting-graph.js +++ b/src/dagre/nesting-graph.js @@ -1,3 +1,4 @@ +import * as _ from 'lodash-es'; import * as util from './util.js'; export { run, cleanup }; @@ -27,20 +28,24 @@ export { run, cleanup }; */ function run(g) { var root = util.addDummyNode(g, 'root', {}, '_root'); - var depths = treeDepths(g); // as Record; - var height = Math.max(...Object.values(depths)) - 1; // Note: depths is an Object not an array + var depths = treeDepths(g); + var height = _.max(_.values(depths)) - 1; // Note: depths is an Object not an array var nodeSep = 2 * height + 1; g.graph().nestingRoot = root; // Multiply minlen by nodeSep to align nodes on non-border ranks. - g.edges().forEach((e) => (g.edge(e).minlen *= nodeSep)); + _.forEach(g.edges(), function (e) { + g.edge(e).minlen *= nodeSep; + }); // Calculate a weight that is sufficient to keep subgraphs vertically compact var weight = sumWeights(g) + 1; // Create border nodes and link them up - g.children().forEach((child) => dfs(g, root, nodeSep, weight, height, depths, child)); + _.forEach(g.children(), function (child) { + dfs(g, root, nodeSep, weight, height, depths, child); + }); // Save the multiplier for node layers for later removal of empty border // layers. @@ -65,7 +70,7 @@ function dfs(g, root, nodeSep, weight, height, depths, v) { g.setParent(bottom, v); label.borderBottom = bottom; - children.forEach((child) => { + _.forEach(children, function (child) { dfs(g, root, nodeSep, weight, height, depths, child); var childNode = g.node(child); @@ -97,23 +102,33 @@ function treeDepths(g) { function dfs(v, depth) { var children = g.children(v); if (children && children.length) { - children.forEach((child) => dfs(child, depth + 1)); + _.forEach(children, function (child) { + dfs(child, depth + 1); + }); } depths[v] = depth; } - g.children().forEach((v) => dfs(v, 1)); + _.forEach(g.children(), function (v) { + dfs(v, 1); + }); return depths; } function sumWeights(g) { - return g.edges().reduce((acc, e) => acc + g.edge(e).weight, 0); + return _.reduce( + g.edges(), + function (acc, e) { + return acc + g.edge(e).weight; + }, + 0, + ); } function cleanup(g) { var graphLabel = g.graph(); g.removeNode(graphLabel.nestingRoot); delete graphLabel.nestingRoot; - g.edges().forEach((e) => { + _.forEach(g.edges(), function (e) { var edge = g.edge(e); if (edge.nestingEdge) { g.removeEdge(e); diff --git a/src/dagre/normalize.js b/src/dagre/normalize.js index 5173a16..de6b94c 100644 --- a/src/dagre/normalize.js +++ b/src/dagre/normalize.js @@ -3,6 +3,7 @@ * * @import { Graph } from '../graphlib/graph.js'; */ +import * as _ from 'lodash-es'; import * as util from './util.js'; export { run, undo }; @@ -25,7 +26,9 @@ export { run, undo }; */ function run(g) { g.graph().dummyChains = []; - g.edges().forEach((edge) => normalizeEdge(g, edge)); + _.forEach(g.edges(), function (edge) { + normalizeEdge(g, edge); + }); } /** @@ -85,7 +88,7 @@ function normalizeEdge(g, e) { } function undo(g) { - g.graph().dummyChains.forEach((v) => { + _.forEach(g.graph().dummyChains, function (v) { var node = g.node(v); var origLabel = node.edgeLabel; var w; diff --git a/src/dagre/order/add-subgraph-constraints.js b/src/dagre/order/add-subgraph-constraints.js index 068945a..f4efbc6 100644 --- a/src/dagre/order/add-subgraph-constraints.js +++ b/src/dagre/order/add-subgraph-constraints.js @@ -1,8 +1,12 @@ -export const addSubgraphConstraints = (g, cg, vs) => { +import * as _ from 'lodash-es'; + +export { addSubgraphConstraints }; + +function addSubgraphConstraints(g, cg, vs) { var prev = {}, rootPrev; - vs.forEach((v) => { + _.forEach(vs, function (v) { var child = g.parent(v), parent, prevChild; @@ -46,4 +50,4 @@ export const addSubgraphConstraints = (g, cg, vs) => { } dfs(undefined); */ -}; +} diff --git a/src/dagre/order/barycenter.js b/src/dagre/order/barycenter.js index 0b57acd..7dabefe 100644 --- a/src/dagre/order/barycenter.js +++ b/src/dagre/order/barycenter.js @@ -1,11 +1,16 @@ -export const barycenter = (g, movable = []) => { - return movable.map((v) => { +import * as _ from 'lodash-es'; + +export { barycenter }; + +function barycenter(g, movable) { + return _.map(movable, function (v) { var inV = g.inEdges(v); if (!inV.length) { return { v: v }; } else { - var result = inV.reduce( - (acc, e) => { + var result = _.reduce( + inV, + function (acc, e) { var edge = g.edge(e), nodeU = g.node(e.v); return { @@ -23,4 +28,4 @@ export const barycenter = (g, movable = []) => { }; } }); -}; +} diff --git a/src/dagre/order/build-layer-graph.js b/src/dagre/order/build-layer-graph.js index 5dadd59..2ac36a7 100644 --- a/src/dagre/order/build-layer-graph.js +++ b/src/dagre/order/build-layer-graph.js @@ -1,6 +1,8 @@ -import { uniqueId } from '../util.js'; +import * as _ from 'lodash-es'; import { Graph } from '../../graphlib/index.js'; +export { buildLayerGraph }; + /* * Constructs a graph that can be used to sort a layer of nodes. The graph will * contain all base and subgraph nodes from the request layer in their original @@ -31,7 +33,7 @@ import { Graph } from '../../graphlib/index.js'; * 5. The weights for copied edges are aggregated as need, since the output * graph is not a multi-graph. */ -export function buildLayerGraph(g, rank, relationship) { +function buildLayerGraph(g, rank, relationship) { var root = createRootNode(g), result = new Graph({ compound: true }) .setGraph({ root: root }) @@ -39,7 +41,7 @@ export function buildLayerGraph(g, rank, relationship) { return g.node(v); }); - g.nodes().forEach((v) => { + _.forEach(g.nodes(), function (v) { var node = g.node(v), parent = g.parent(v); @@ -48,10 +50,10 @@ export function buildLayerGraph(g, rank, relationship) { result.setParent(v, parent || root); // This assumes we have only short edges! - g[relationship](v).forEach((e) => { + _.forEach(g[relationship](v), function (e) { var u = e.v === v ? e.w : e.v, edge = result.edge(u, v), - weight = edge !== undefined ? edge.weight : 0; + weight = !_.isUndefined(edge) ? edge.weight : 0; result.setEdge(u, v, { weight: g.edge(e).weight + weight }); }); @@ -69,6 +71,6 @@ export function buildLayerGraph(g, rank, relationship) { function createRootNode(g) { var v; - while (g.hasNode((v = uniqueId('_root')))); + while (g.hasNode((v = _.uniqueId('_root')))); return v; } diff --git a/src/dagre/order/cross-count.js b/src/dagre/order/cross-count.js index 70f9fee..cf9d195 100644 --- a/src/dagre/order/cross-count.js +++ b/src/dagre/order/cross-count.js @@ -1,4 +1,4 @@ -import { zipObject } from '../util.js'; +import * as _ from 'lodash-es'; export { crossCount }; @@ -30,41 +30,53 @@ function twoLayerCrossCount(g, northLayer, southLayer) { // Sort all of the edges between the north and south layers by their position // in the north layer and then the south. Map these edges to the position of // their head in the south layer. - var southPos = zipObject( + var southPos = _.zipObject( southLayer, - southLayer.map((v, i) => i), + _.map(southLayer, function (v, i) { + return i; + }), + ); + var southEntries = _.flatten( + _.map(northLayer, function (v) { + return _.sortBy( + _.map(g.outEdges(v), function (e) { + return { pos: southPos[e.w], weight: g.edge(e).weight }; + }), + 'pos', + ); + }), ); - var southEntries = northLayer.flatMap((v) => { - return g - .outEdges(v) - .map((e) => { - return { pos: southPos[e.w], weight: g.edge(e).weight }; - }) - .sort((a, b) => a.pos - b.pos); - }); // Build the accumulator tree var firstIndex = 1; while (firstIndex < southLayer.length) firstIndex <<= 1; var treeSize = 2 * firstIndex - 1; firstIndex -= 1; - var tree = new Array(treeSize).fill(0); + var tree = _.map(new Array(treeSize), function () { + return 0; + }); // Calculate the weighted crossings var cc = 0; - southEntries.forEach((entry) => { - var index = entry.pos + firstIndex; - tree[index] += entry.weight; - var weightSum = 0; - while (index > 0) { - if (index % 2) { - weightSum += tree[index + 1]; - } - index = (index - 1) >> 1; + _.forEach( + // @ts-expect-error + southEntries.forEach(function (entry) { + var index = entry.pos + firstIndex; tree[index] += entry.weight; - } - cc += entry.weight * weightSum; - }); + var weightSum = 0; + // @ts-expect-error + while (index > 0) { + // @ts-expect-error + if (index % 2) { + weightSum += tree[index + 1]; + } + // @ts-expect-error + index = (index - 1) >> 1; + tree[index] += entry.weight; + } + cc += entry.weight * weightSum; + }), + ); return cc; } diff --git a/src/dagre/order/index.js b/src/dagre/order/index.js index 12ec8cf..f29508a 100644 --- a/src/dagre/order/index.js +++ b/src/dagre/order/index.js @@ -1,3 +1,4 @@ +import * as _ from 'lodash-es'; import { Graph } from '../../graphlib/index.js'; import * as util from '../util.js'; import { addSubgraphConstraints } from './add-subgraph-constraints.js'; @@ -25,8 +26,8 @@ export { order }; */ function order(g) { var maxRank = util.maxRank(g), - downLayerGraphs = buildLayerGraphs(g, util.range(1, maxRank + 1), 'inEdges'), - upLayerGraphs = buildLayerGraphs(g, util.range(maxRank - 1, -1, -1), 'outEdges'); + downLayerGraphs = buildLayerGraphs(g, _.range(1, maxRank + 1), 'inEdges'), + upLayerGraphs = buildLayerGraphs(g, _.range(maxRank - 1, -1, -1), 'outEdges'); var layering = initOrder(g); assignOrder(g, layering); @@ -41,7 +42,7 @@ function order(g) { var cc = crossCount(g, layering); if (cc < bestCC) { lastBest = 0; - best = Object.assign({}, layering); + best = _.cloneDeep(layering); bestCC = cc; } } @@ -50,22 +51,27 @@ function order(g) { } function buildLayerGraphs(g, ranks, relationship) { - return ranks.map((rank) => buildLayerGraph(g, rank, relationship)); + return _.map(ranks, function (rank) { + return buildLayerGraph(g, rank, relationship); + }); } function sweepLayerGraphs(layerGraphs, biasRight) { var cg = new Graph(); - layerGraphs.forEach((lg) => { + _.forEach(layerGraphs, function (lg) { var root = lg.graph().root; var sorted = sortSubgraph(lg, root, cg, biasRight); - sorted.vs.forEach((v, i) => (lg.node(v).order = i)); + _.forEach(sorted.vs, function (v, i) { + lg.node(v).order = i; + }); addSubgraphConstraints(lg, cg, sorted.vs); }); } function assignOrder(g, layering) { - // Object.values(layering).forEach((layer: [string, number][]) => { - Object.values(layering).forEach((layer) => { - layer.forEach((v, i) => (g.node(v).order = i)); + _.forEach(layering, function (layer) { + _.forEach(layer, function (v, i) { + g.node(v).order = i; + }); }); } diff --git a/src/dagre/order/init-order.js b/src/dagre/order/init-order.js index 1ca1fb1..d6916e8 100644 --- a/src/dagre/order/init-order.js +++ b/src/dagre/order/init-order.js @@ -1,4 +1,4 @@ -import { range } from '../util.js'; +import * as _ from 'lodash-es'; /* * Assigns an initial order value for each node by performing a DFS search @@ -13,20 +13,30 @@ import { range } from '../util.js'; */ export function initOrder(g) { var visited = {}; - var simpleNodes = g.nodes().filter((v) => !g.children(v).length); - var maxRank = Math.max(...simpleNodes.map((v) => g.node(v).rank)); - var layers = range(maxRank + 1).map(() => []); + var simpleNodes = _.filter(g.nodes(), function (v) { + return !g.children(v).length; + }); + var maxRank = _.max( + _.map(simpleNodes, function (v) { + return g.node(v).rank; + }), + ); + var layers = _.map(_.range(maxRank + 1), function () { + return []; + }); function dfs(v) { - if (visited[v]) return; + if (_.has(visited, v)) return; visited[v] = true; var node = g.node(v); layers[node.rank].push(v); - g.successors(v).forEach(dfs); + _.forEach(g.successors(v), dfs); } - var orderedVs = simpleNodes.sort((a, b) => g.node(a).rank - g.node(b).rank); - orderedVs.forEach(dfs); + var orderedVs = _.sortBy(simpleNodes, function (v) { + return g.node(v).rank; + }); + _.forEach(orderedVs, dfs); return layers; } diff --git a/src/dagre/order/resolve-conflicts.js b/src/dagre/order/resolve-conflicts.js index 074a569..cca18fa 100644 --- a/src/dagre/order/resolve-conflicts.js +++ b/src/dagre/order/resolve-conflicts.js @@ -1,4 +1,4 @@ -import { pick } from '../util.js'; +import * as _ from 'lodash-es'; export { resolveConflicts }; @@ -29,7 +29,7 @@ export { resolveConflicts }; */ function resolveConflicts(entries, cg) { var mappedEntries = {}; - entries.forEach((entry, i) => { + _.forEach(entries, function (entry, i) { var tmp = (mappedEntries[entry.v] = { indegree: 0, in: [], @@ -37,7 +37,7 @@ function resolveConflicts(entries, cg) { vs: [entry.v], i: i, }); - if (entry.barycenter !== undefined) { + if (!_.isUndefined(entry.barycenter)) { // @ts-expect-error tmp.barycenter = entry.barycenter; // @ts-expect-error @@ -45,16 +45,19 @@ function resolveConflicts(entries, cg) { } }); - cg.edges().forEach((e) => { + _.forEach(cg.edges(), function (e) { var entryV = mappedEntries[e.v]; var entryW = mappedEntries[e.w]; - if (entryV !== undefined && entryW !== undefined) { + if (!_.isUndefined(entryV) && !_.isUndefined(entryW)) { entryW.indegree++; entryV.out.push(mappedEntries[e.w]); } }); - var sourceSet = Object.values(mappedEntries).filter((entry) => !entry.indegree); + var sourceSet = _.filter(mappedEntries, function (entry) { + // @ts-expect-error + return !entry.indegree; + }); return doResolveConflicts(sourceSet); } @@ -68,8 +71,8 @@ function doResolveConflicts(sourceSet) { return; } if ( - uEntry.barycenter === undefined || - vEntry.barycenter === undefined || + _.isUndefined(uEntry.barycenter) || + _.isUndefined(vEntry.barycenter) || uEntry.barycenter >= vEntry.barycenter ) { mergeEntries(vEntry, uEntry); @@ -89,15 +92,18 @@ function doResolveConflicts(sourceSet) { while (sourceSet.length) { var entry = sourceSet.pop(); entries.push(entry); - entry['in'].reverse().forEach(handleIn(entry)); - entry.out.forEach(handleOut(entry)); + _.forEach(entry['in'].reverse(), handleIn(entry)); + _.forEach(entry.out, handleOut(entry)); } - return entries - .filter((entry) => !entry.merged) - .map((entry) => { - return pick(entry, ['vs', 'i', 'barycenter', 'weight']); - }); + return _.map( + _.filter(entries, function (entry) { + return !entry.merged; + }), + function (entry) { + return _.pick(entry, ['vs', 'i', 'barycenter', 'weight']); + }, + ); } function mergeEntries(target, source) { diff --git a/src/dagre/order/sort-subgraph.js b/src/dagre/order/sort-subgraph.js index 2c21c0a..2467304 100644 --- a/src/dagre/order/sort-subgraph.js +++ b/src/dagre/order/sort-subgraph.js @@ -1,3 +1,4 @@ +import * as _ from 'lodash-es'; import { barycenter } from './barycenter.js'; import { resolveConflicts } from './resolve-conflicts.js'; import { sort } from './sort.js'; @@ -12,11 +13,13 @@ function sortSubgraph(g, v, cg, biasRight) { var subgraphs = {}; if (bl) { - movable = movable.filter((w) => w !== bl && w !== br); + movable = _.filter(movable, function (w) { + return w !== bl && w !== br; + }); } var barycenters = barycenter(g, movable); - barycenters.forEach((entry) => { + _.forEach(barycenters, function (entry) { if (g.children(entry.v).length) { var subgraphResult = sortSubgraph(g, entry.v, cg, biasRight); subgraphs[entry.v] = subgraphResult; @@ -32,7 +35,7 @@ function sortSubgraph(g, v, cg, biasRight) { var result = sort(entries, biasRight); if (bl) { - result.vs = [bl, result.vs, br].flat(1); + result.vs = _.flatten([bl, result.vs, br]); if (g.predecessors(bl).length) { var blPred = g.node(g.predecessors(bl)[0]), brPred = g.node(g.predecessors(br)[0]); @@ -50,18 +53,20 @@ function sortSubgraph(g, v, cg, biasRight) { } function expandSubgraphs(entries, subgraphs) { - entries.forEach((entry) => { - entry.vs = entry.vs.flatMap((v) => { - if (subgraphs[v]) { - return subgraphs[v].vs; - } - return v; - }); + _.forEach(entries, function (entry) { + entry.vs = _.flatten( + entry.vs.map(function (v) { + if (subgraphs[v]) { + return subgraphs[v].vs; + } + return v; + }), + ); }); } function mergeBarycenters(target, other) { - if (target.barycenter !== undefined) { + if (!_.isUndefined(target.barycenter)) { target.barycenter = (target.barycenter * target.weight + other.barycenter * other.weight) / (target.weight + other.weight); diff --git a/src/dagre/order/sort.js b/src/dagre/order/sort.js index 1f0c29a..539978b 100644 --- a/src/dagre/order/sort.js +++ b/src/dagre/order/sort.js @@ -1,3 +1,4 @@ +import * as _ from 'lodash-es'; import * as util from '../util.js'; export { sort }; @@ -7,7 +8,9 @@ function sort(entries, biasRight) { return Object.prototype.hasOwnProperty.call(entry, 'barycenter'); }); var sortable = parts.lhs, - unsortable = parts.rhs.sort((a, b) => b.i - a.i), + unsortable = _.sortBy(parts.rhs, function (entry) { + return -entry.i; + }), vs = [], sum = 0, weight = 0, @@ -17,7 +20,7 @@ function sort(entries, biasRight) { vsIndex = consumeUnsortable(vs, unsortable, vsIndex); - sortable.forEach((entry) => { + _.forEach(sortable, function (entry) { vsIndex += entry.vs.length; vs.push(entry.vs); sum += entry.barycenter * entry.weight; @@ -25,7 +28,7 @@ function sort(entries, biasRight) { vsIndex = consumeUnsortable(vs, unsortable, vsIndex); }); - var result = { vs: vs.flat(1) }; // as { vs: string[]; barycenter?: number; weight?: number }; + var result = { vs: _.flatten(vs) }; if (weight) { result.barycenter = sum / weight; result.weight = weight; @@ -35,7 +38,7 @@ function sort(entries, biasRight) { function consumeUnsortable(vs, unsortable, index) { var last; - while (unsortable.length && (last = unsortable[unsortable.length - 1]).i <= index) { + while (unsortable.length && (last = _.last(unsortable)).i <= index) { unsortable.pop(); vs.push(last.vs); index++; diff --git a/src/dagre/parent-dummy-chains.js b/src/dagre/parent-dummy-chains.js index b2515d1..367fd37 100644 --- a/src/dagre/parent-dummy-chains.js +++ b/src/dagre/parent-dummy-chains.js @@ -1,9 +1,11 @@ +import * as _ from 'lodash-es'; + export { parentDummyChains }; function parentDummyChains(g) { var postorderNums = postorder(g); - g.graph().dummyChains.forEach((v) => { + _.forEach(g.graph().dummyChains, function (v) { var node = g.node(v); var edgeObj = node.edgeObj; var pathData = findPath(g, postorderNums, edgeObj.v, edgeObj.w); @@ -75,10 +77,10 @@ function postorder(g) { function dfs(v) { var low = lim; - g.children(v).forEach(dfs); + _.forEach(g.children(v), dfs); result[v] = { low: low, lim: lim++ }; } - g.children().forEach(dfs); + _.forEach(g.children(), dfs); return result; } diff --git a/src/dagre/position/bk.js b/src/dagre/position/bk.js index 3959e2a..d4aabdc 100644 --- a/src/dagre/position/bk.js +++ b/src/dagre/position/bk.js @@ -1,9 +1,10 @@ +import * as _ from 'lodash-es'; import { Graph } from '../../graphlib/index.js'; import * as util from '../util.js'; /* - * This module provides coordinate assignment based on Brandes and Köpf, 'Fast - * and Simple Horizontal Coordinate Assignment.' + * This module provides coordinate assignment based on Brandes and Köpf, "Fast + * and Simple Horizontal Coordinate Assignment." */ export { @@ -20,10 +21,10 @@ export { }; /* - * Marks all edges in the graph with a type-1 conflict with the 'type1Conflict' + * Marks all edges in the graph with a type-1 conflict with the "type1Conflict" * property. A type-1 conflict is one where a non-inner segment crosses an * inner segment. An inner segment is an edge with both incident nodes marked - * with the 'dummy' property. + * with the "dummy" property. * * This algorithm scans layer by layer, starting with the second, for type-1 * conflicts between the current layer and the previous layer. For each layer @@ -40,22 +41,22 @@ function findType1Conflicts(g, layering) { var conflicts = {}; function visitLayer(prevLayer, layer) { - let // last visited node in the previous layer that is incident on an inner + var // last visited node in the previous layer that is incident on an inner // segment. k0 = 0, // Tracks the last node in this layer scanned for crossings with a type-1 // segment. scanPos = 0, prevLayerLength = prevLayer.length, - lastNode = layer[layer.length - 1]; + lastNode = _.last(layer); - layer.forEach((v, i) => { + _.forEach(layer, function (v, i) { var w = findOtherInnerSegmentNode(g, v), k1 = w ? g.node(w).order : prevLayerLength; if (w || v === lastNode) { - layer.slice(scanPos, i + 1).forEach((scanNode) => { - g.predecessors(scanNode).forEach((u) => { + _.forEach(layer.slice(scanPos, i + 1), function (scanNode) { + _.forEach(g.predecessors(scanNode), function (u) { var uLabel = g.node(u), uPos = uLabel.order; if ((uPos < k0 || k1 < uPos) && !(uLabel.dummy && g.node(scanNode).dummy)) { @@ -63,6 +64,7 @@ function findType1Conflicts(g, layering) { } }); }); + // @ts-expect-error scanPos = i + 1; k0 = k1; } @@ -71,7 +73,7 @@ function findType1Conflicts(g, layering) { return layer; } - if (layering.length > 0) layering.reduce(visitLayer); + _.reduce(layering, visitLayer); return conflicts; } @@ -80,10 +82,10 @@ function findType2Conflicts(g, layering) { function scan(south, southPos, southEnd, prevNorthBorder, nextNorthBorder) { var v; - util.range(southPos, southEnd).forEach((i) => { + _.forEach(_.range(southPos, southEnd), function (i) { v = south[i]; if (g.node(v).dummy) { - g.predecessors(v).forEach((u) => { + _.forEach(g.predecessors(v), function (u) { var uNode = g.node(u); if (uNode.dummy && (uNode.order < prevNorthBorder || uNode.order > nextNorthBorder)) { addConflict(conflicts, u, v); @@ -96,14 +98,15 @@ function findType2Conflicts(g, layering) { function visitLayer(north, south) { var prevNorthPos = -1, nextNorthPos, - southPos = 0; // as number | string; + southPos = 0; - south.forEach((v, southLookahead) => { + _.forEach(south, function (v, southLookahead) { if (g.node(v).dummy === 'border') { var predecessors = g.predecessors(v); if (predecessors.length) { nextNorthPos = g.node(predecessors[0]).order; scan(south, southPos, southLookahead, prevNorthPos, nextNorthPos); + // @ts-expect-error southPos = southLookahead; prevNorthPos = nextNorthPos; } @@ -114,13 +117,15 @@ function findType2Conflicts(g, layering) { return south; } - if (layering.length > 0) layering.reduce(visitLayer); + _.reduce(layering, visitLayer); return conflicts; } function findOtherInnerSegmentNode(g, v) { if (g.node(v).dummy) { - return g.predecessors(v).find((u) => g.node(u).dummy); + return _.find(g.predecessors(v), function (u) { + return g.node(u).dummy; + }); } } @@ -148,7 +153,7 @@ function hasConflict(conflicts, v, w) { } /* - * Try to align nodes into vertical 'blocks' where possible. This algorithm + * Try to align nodes into vertical "blocks" where possible. This algorithm * attempts to align a node with one of its median neighbors. If the edge * connecting a neighbor is a type-1 conflict then we ignore that possibility. * If a previous node has already formed a block with a node after the node @@ -163,20 +168,22 @@ function verticalAlignment(g, layering, conflicts, neighborFn) { // We cache the position here based on the layering because the graph and // layering may be out of sync. The layering matrix is manipulated to // generate different extreme alignments. - layering.forEach((layer) => { - layer.forEach((v, order) => { + _.forEach(layering, function (layer) { + _.forEach(layer, function (v, order) { root[v] = v; align[v] = v; pos[v] = order; }); }); - layering.forEach((layer) => { + _.forEach(layering, function (layer) { var prevIdx = -1; - layer.forEach((v) => { + _.forEach(layer, function (v) { var ws = neighborFn(v); if (ws.length) { - ws = ws.sort((a, b) => pos[a] - pos[b]); + ws = _.sortBy(ws, function (w) { + return pos[w]; + }); var mp = (ws.length - 1) / 2; for (var i = Math.floor(mp), il = Math.ceil(mp); i <= il; ++i) { var w = ws[i]; @@ -222,7 +229,7 @@ function horizontalCompaction(g, layering, root, align, reverseSep) { // First pass, assign smallest coordinates function pass1(elem) { - xs[elem] = blockG.inEdges(elem).reduce((acc, e) => { + xs[elem] = blockG.inEdges(elem).reduce(function (acc, e) { return Math.max(acc, xs[e.v] + blockG.edge(e)); }, 0); } @@ -243,7 +250,9 @@ function horizontalCompaction(g, layering, root, align, reverseSep) { iterate(pass2, blockG.successors.bind(blockG)); // Assign x coordinates to all nodes - Object.keys(align).forEach((v) => (xs[v] = xs[root[v]])); + _.forEach(align, function (v) { + xs[v] = xs[root[v]]; + }); return xs; } @@ -253,9 +262,9 @@ function buildBlockGraph(g, layering, root, reverseSep) { graphLabel = g.graph(), sepFn = sep(graphLabel.nodesep, graphLabel.edgesep, reverseSep); - layering.forEach((layer) => { + _.forEach(layering, function (layer) { var u; - layer.forEach((v) => { + _.forEach(layer, function (v) { var vRoot = root[v]; blockGraph.setNode(vRoot); if (u) { @@ -274,28 +283,19 @@ function buildBlockGraph(g, layering, root, reverseSep) { * Returns the alignment that has the smallest width of the given alignments. */ function findSmallestWidthAlignment(g, xss) { - // : Record - return Object.values(xss).reduce( - (currentMinAndXs, xs) => { - var max = Number.NEGATIVE_INFINITY; - var min = Number.POSITIVE_INFINITY; - - Object.entries(xs).forEach(([v, x]) => { - var halfWidth = width(g, v) / 2; - - max = Math.max(x + halfWidth, max); - min = Math.min(x - halfWidth, min); - }); - - const newMin = max - min; - if (newMin < currentMinAndXs[0]) { - currentMinAndXs = [newMin, xs]; - } + return _.minBy(_.values(xss), function (xs) { + var max = Number.NEGATIVE_INFINITY; + var min = Number.POSITIVE_INFINITY; - return currentMinAndXs; - }, - [Number.POSITIVE_INFINITY, null], - )[1]; + _.forIn(xs, function (x, v) { + var halfWidth = width(g, v) / 2; + + max = Math.max(x + halfWidth, max); + min = Math.min(x - halfWidth, min); + }); + + return max - min; + }); } /* @@ -306,44 +306,35 @@ function findSmallestWidthAlignment(g, xss) { * coordinate of the smallest width alignment. */ function alignCoordinates(xss, alignTo) { - // alignTo: Record - let alignToMin = Number.POSITIVE_INFINITY, - alignToMax = Number.NEGATIVE_INFINITY; + var alignToVals = _.values(alignTo), + alignToMin = _.min(alignToVals), + alignToMax = _.max(alignToVals); - if (alignTo) { - const alignToVals = Object.values(alignTo); - - alignToMin = Math.min(...alignToVals); - alignToMax = Math.max(...alignToVals); - } - - ['u', 'd'].forEach((vert) => { - ['l', 'r'].forEach((horiz) => { + _.forEach(['u', 'd'], function (vert) { + _.forEach(['l', 'r'], function (horiz) { var alignment = vert + horiz, - xs = xss[alignment]; + xs = xss[alignment], + delta; if (xs === alignTo) return; - var xsVals = Object.values(xs); // as number[]; - let delta = alignToMin - Math.min(...xsVals); - if (horiz !== 'l') { - delta = alignToMax - Math.max(...xsVals); - } + var xsVals = _.values(xs); + delta = horiz === 'l' ? alignToMin - _.min(xsVals) : alignToMax - _.max(xsVals); if (delta) { - xss[alignment] = util.mapValues(xs, (x) => x + delta); + xss[alignment] = _.mapValues(xs, function (x) { + return x + delta; + }); } }); }); } function balance(xss, align) { - return util.mapValues(xss.ul, (num, v) => { + return _.mapValues(xss.ul, function (ignore, v) { if (align) { return xss[align.toLowerCase()][v]; } else { - var xs = Object.values(xss) - .map((xs) => xs[v]) - .sort((a, b) => a - b); + var xs = _.sortBy(_.map(xss, v)); return (xs[1] + xs[2]) / 2; } }); @@ -351,16 +342,16 @@ function balance(xss, align) { function positionX(g) { var layering = util.buildLayerMatrix(g); - var conflicts = Object.assign(findType1Conflicts(g, layering), findType2Conflicts(g, layering)); + var conflicts = _.merge(findType1Conflicts(g, layering), findType2Conflicts(g, layering)); var xss = {}; var adjustedLayering; - ['u', 'd'].forEach((vert) => { - adjustedLayering = vert === 'u' ? layering : Object.values(layering).reverse(); - ['l', 'r'].forEach((horiz) => { + _.forEach(['u', 'd'], function (vert) { + adjustedLayering = vert === 'u' ? layering : _.values(layering).reverse(); + _.forEach(['l', 'r'], function (horiz) { if (horiz === 'r') { - adjustedLayering = adjustedLayering.map((inner) => { - return Object.values(inner).reverse(); + adjustedLayering = _.map(adjustedLayering, function (inner) { + return _.values(inner).reverse(); }); } @@ -368,9 +359,10 @@ function positionX(g) { var align = verticalAlignment(g, adjustedLayering, conflicts, neighborFn); var xs = horizontalCompaction(g, adjustedLayering, align.root, align.align, horiz === 'r'); if (horiz === 'r') { - xs = util.mapValues(xs, (x) => -x); + xs = _.mapValues(xs, function (x) { + return -x; + }); } - xss[vert + horiz] = xs; }); }); @@ -381,7 +373,7 @@ function positionX(g) { } function sep(nodeSep, edgeSep, reverseSep) { - return (g, v, w) => { + return function (g, v, w) { var vLabel = g.node(v); var wLabel = g.node(w); var sum = 0; diff --git a/src/dagre/position/index.js b/src/dagre/position/index.js index 53b49bc..68ee820 100644 --- a/src/dagre/position/index.js +++ b/src/dagre/position/index.js @@ -1,3 +1,4 @@ +import * as _ from 'lodash-es'; import * as util from '../util.js'; import { positionX } from './bk.js'; @@ -7,23 +8,24 @@ function position(g) { g = util.asNonCompoundGraph(g); positionY(g); - Object.entries(positionX(g)).forEach(([v, x]) => (g.node(v).x = x)); + _.forOwn(positionX(g), function (x, v) { + g.node(v).x = x; + }); } function positionY(g) { var layering = util.buildLayerMatrix(g); var rankSep = g.graph().ranksep; var prevY = 0; - layering.forEach((layer) => { - const maxHeight = layer.reduce((acc, v) => { - const height = g.node(v).height; - if (acc > height) { - return acc; - } else { - return height; - } - }, 0); - layer.forEach((v) => (g.node(v).y = prevY + maxHeight / 2)); + _.forEach(layering, function (layer) { + var maxHeight = _.max( + _.map(layer, function (v) { + return g.node(v).height; + }), + ); + _.forEach(layer, function (v) { + g.node(v).y = prevY + maxHeight / 2; + }); prevY += maxHeight + rankSep; }); } diff --git a/src/dagre/rank/feasible-tree.js b/src/dagre/rank/feasible-tree.js index a19d060..fdb2b01 100644 --- a/src/dagre/rank/feasible-tree.js +++ b/src/dagre/rank/feasible-tree.js @@ -1,3 +1,4 @@ +import * as _ from 'lodash-es'; import { Graph } from '../../graphlib/index.js'; import { slack } from './util.js'; @@ -52,7 +53,7 @@ function feasibleTree(g) { */ function tightTree(t, g) { function dfs(v) { - g.nodeEdges(v).forEach((e) => { + _.forEach(g.nodeEdges(v), function (e) { var edgeV = e.v, w = v === edgeV ? e.w : edgeV; if (!t.hasNode(w) && !slack(g, e)) { @@ -63,7 +64,7 @@ function tightTree(t, g) { }); } - t.nodes().forEach(dfs); + _.forEach(t.nodes(), dfs); return t.nodeCount(); } @@ -72,25 +73,15 @@ function tightTree(t, g) { * it. */ function findMinSlackEdge(t, g) { - const edges = g.edges(); - - return edges.reduce( - (acc, edge) => { - let edgeSlack = Number.POSITIVE_INFINITY; - if (t.hasNode(edge.v) !== t.hasNode(edge.w)) { - edgeSlack = slack(g, edge); - } - - if (edgeSlack < acc[0]) { - return [edgeSlack, edge]; - } - - return acc; - }, - [Number.POSITIVE_INFINITY, null], - )[1]; + return _.minBy(g.edges(), function (e) { + if (t.hasNode(e.v) !== t.hasNode(e.w)) { + return slack(g, e); + } + }); } function shiftRanks(t, g, delta) { - t.nodes().forEach((v) => (g.node(v).rank += delta)); + _.forEach(t.nodes(), function (v) { + g.node(v).rank += delta; + }); } diff --git a/src/dagre/rank/network-simplex.js b/src/dagre/rank/network-simplex.js index 9b51756..e4913e8 100644 --- a/src/dagre/rank/network-simplex.js +++ b/src/dagre/rank/network-simplex.js @@ -1,3 +1,4 @@ +import * as _ from 'lodash-es'; import * as alg from '../../graphlib/alg/index.js'; import { simplify } from '../util.js'; import { feasibleTree } from './feasible-tree.js'; @@ -66,7 +67,9 @@ function networkSimplex(g) { function initCutValues(t, g) { var vs = alg.postorder(t, t.nodes()); vs = vs.slice(0, vs.length - 1); - vs.forEach((v) => assignCutValue(t, g, v)); + _.forEach(vs, function (v) { + assignCutValue(t, g, v); + }); } function assignCutValue(t, g, child) { @@ -96,7 +99,7 @@ function calcCutValue(t, g, child) { cutValue = graphEdge.weight; - g.nodeEdges(child).forEach((e) => { + _.forEach(g.nodeEdges(child), function (e) { var isOutEdge = e.v === child, other = isOutEdge ? e.w : e.v; @@ -127,7 +130,7 @@ function dfsAssignLowLim(tree, visited, nextLim, v, parent) { var label = tree.node(v); visited[v] = true; - tree.neighbors(v).forEach((w) => { + _.forEach(tree.neighbors(v), function (w) { if (!Object.prototype.hasOwnProperty.call(visited, w)) { nextLim = dfsAssignLowLim(tree, visited, nextLim, w, v); } @@ -146,7 +149,9 @@ function dfsAssignLowLim(tree, visited, nextLim, v, parent) { } function leaveEdge(tree) { - return tree.edges().find((e) => tree.edge(e).cutvalue < 0); + return _.find(tree.edges(), function (e) { + return tree.edge(e).cutvalue < 0; + }); } function enterEdge(t, g, edge) { @@ -173,24 +178,16 @@ function enterEdge(t, g, edge) { flip = true; } - const candidates = g.edges().filter((edge) => { + var candidates = _.filter(g.edges(), function (edge) { return ( flip === isDescendant(t, t.node(edge.v), tailLabel) && flip !== isDescendant(t, t.node(edge.w), tailLabel) ); }); - if (candidates.length > 0) { - return candidates.reduce((acc, edge) => { - if (slack(g, edge) < slack(g, acc)) { - return edge; - } - - return acc; - }); - } else { - return undefined; - } + return _.minBy(candidates, function (edge) { + return slack(g, edge); + }); } function exchangeEdges(t, g, e, f) { @@ -204,10 +201,12 @@ function exchangeEdges(t, g, e, f) { } function updateRanks(t, g) { - var root = t.nodes().find((v) => !g.node(v).parent); + var root = _.find(t.nodes(), function (v) { + return !g.node(v).parent; + }); var vs = alg.preorder(t, root); vs = vs.slice(1); - vs.forEach((v) => { + _.forEach(vs, function (v) { var parent = t.node(v).parent, edge = g.edge(v, parent), flipped = false; diff --git a/src/dagre/rank/util.js b/src/dagre/rank/util.js index 5cb536c..8759a9f 100644 --- a/src/dagre/rank/util.js +++ b/src/dagre/rank/util.js @@ -1,3 +1,5 @@ +import * as _ from 'lodash-es'; + export { longestPath, slack }; /* @@ -31,24 +33,25 @@ function longestPath(g) { } visited[v] = true; - var rank = Math.min( - ...g.outEdges(v).map((e) => { - if (e == null) { - return Number.POSITIVE_INFINITY; - } - + var rank = _.min( + _.map(g.outEdges(v), function (e) { return dfs(e.w) - g.edge(e).minlen; }), ); - if (rank === Number.POSITIVE_INFINITY) { + if ( + rank === Number.POSITIVE_INFINITY || // return value of _.map([]) for Lodash 3 + rank === undefined || // return value of _.map([]) for Lodash 4 + rank === null + ) { + // return value of _.map([null]) rank = 0; } return (label.rank = rank); } - g.sources().forEach(dfs); + _.forEach(g.sources(), dfs); } /* diff --git a/src/dagre/util.js b/src/dagre/util.js index d2186fb..f526e82 100644 --- a/src/dagre/util.js +++ b/src/dagre/util.js @@ -1,3 +1,4 @@ +import * as _ from 'lodash-es'; import { Graph } from '../graphlib/index.js'; export { @@ -15,11 +16,6 @@ export { partition, time, notime, - uniqueId, - range, - pick, - mapValues, - zipObject, }; /* @@ -28,7 +24,7 @@ export { function addDummyNode(g, type, attrs, name) { var v; do { - v = uniqueId(name); + v = _.uniqueId(name); } while (g.hasNode(v)); attrs.dummy = type; @@ -42,8 +38,10 @@ function addDummyNode(g, type, attrs, name) { */ function simplify(g) { var simplified = new Graph().setGraph(g.graph()); - g.nodes().forEach((v) => simplified.setNode(v, g.node(v))); - g.edges().forEach((e) => { + _.forEach(g.nodes(), function (v) { + simplified.setNode(v, g.node(v)); + }); + _.forEach(g.edges(), function (e) { var simpleLabel = simplified.edge(e.v, e.w) || { weight: 0, minlen: 1 }; var label = g.edge(e); simplified.setEdge(e.v, e.w, { @@ -56,37 +54,37 @@ function simplify(g) { function asNonCompoundGraph(g) { var simplified = new Graph({ multigraph: g.isMultigraph() }).setGraph(g.graph()); - g.nodes().forEach((v) => { + _.forEach(g.nodes(), function (v) { if (!g.children(v).length) { simplified.setNode(v, g.node(v)); } }); - g.edges().forEach((e) => { + _.forEach(g.edges(), function (e) { simplified.setEdge(e, g.edge(e)); }); return simplified; } function successorWeights(g) { - var weightMap = g.nodes().map((v) => { + var weightMap = _.map(g.nodes(), function (v) { var sucs = {}; - g.outEdges(v).forEach((e) => { + _.forEach(g.outEdges(v), function (e) { sucs[e.w] = (sucs[e.w] || 0) + g.edge(e).weight; }); return sucs; }); - return zipObject(g.nodes(), weightMap); + return _.zipObject(g.nodes(), weightMap); } function predecessorWeights(g) { - var weightMap = g.nodes().map((v) => { + var weightMap = _.map(g.nodes(), function (v) { var preds = {}; - g.inEdges(v).forEach((e) => { + _.forEach(g.inEdges(v), function (e) { preds[e.v] = (preds[e.v] || 0) + g.edge(e).weight; }); return preds; }); - return zipObject(g.nodes(), weightMap); + return _.zipObject(g.nodes(), weightMap); } /* @@ -129,15 +127,17 @@ function intersectRect(rect, point) { } /* - * Given a DAG with each node assigned 'rank' and 'order' properties, this + * Given a DAG with each node assigned "rank" and "order" properties, this * function will produce a matrix with the ids of each node. */ function buildLayerMatrix(g) { - var layering = range(maxRank(g) + 1).map(() => []); - g.nodes().forEach((v) => { + var layering = _.map(_.range(maxRank(g) + 1), function () { + return []; + }); + _.forEach(g.nodes(), function (v) { var node = g.node(v); var rank = node.rank; - if (rank !== undefined) { + if (!_.isUndefined(rank)) { layering[rank][node.order] = v; } }); @@ -149,19 +149,14 @@ function buildLayerMatrix(g) { * rank(v) >= 0 and at least one node w has rank(w) = 0. */ function normalizeRanks(g) { - var min = Math.min( - ...g.nodes().map((v) => { - var rank = g.node(v).rank; - if (rank === undefined) { - return Number.MAX_VALUE; - } - - return rank; + var min = _.min( + _.map(g.nodes(), function (v) { + return g.node(v).rank; }), ); - g.nodes().forEach((v) => { - const node = g.node(v); - if (Object.prototype.hasOwnProperty.call(node, 'rank')) { + _.forEach(g.nodes(), function (v) { + var node = g.node(v); + if (_.has(node, 'rank')) { node.rank -= min; } }); @@ -169,10 +164,14 @@ function normalizeRanks(g) { function removeEmptyRanks(g) { // Ranks may not start at 0, so we need to offset them - var offset = Math.min(...g.nodes().map((v) => g.node(v).rank)); + var offset = _.min( + _.map(g.nodes(), function (v) { + return g.node(v).rank; + }), + ); var layers = []; - g.nodes().forEach((v) => { + _.forEach(g.nodes(), function (v) { var rank = g.node(v).rank - offset; if (!layers[rank]) { layers[rank] = []; @@ -182,11 +181,13 @@ function removeEmptyRanks(g) { var delta = 0; var nodeRankFactor = g.graph().nodeRankFactor; - Array.from(layers).forEach((vs, i) => { - if (vs === undefined && i % nodeRankFactor !== 0) { + _.forEach(layers, function (vs, i) { + if (_.isUndefined(vs) && i % nodeRankFactor !== 0) { --delta; - } else if (vs !== undefined && delta) { - vs.forEach((v) => (g.node(v).rank += delta)); + } else if (delta) { + _.forEach(vs, function (v) { + g.node(v).rank += delta; + }); } }); } @@ -195,7 +196,7 @@ function addBorderNode(g, prefix, rank, order) { var node = { width: 0, height: 0, - }; // as { width: number; height: number; rank?: number; order?: number }; + }; if (arguments.length >= 4) { node.rank = rank; node.order = order; @@ -204,14 +205,12 @@ function addBorderNode(g, prefix, rank, order) { } function maxRank(g) { - return Math.max( - ...g.nodes().map((v) => { + return _.max( + _.map(g.nodes(), function (v) { var rank = g.node(v).rank; - if (rank === undefined) { - return Number.MIN_VALUE; + if (!_.isUndefined(rank)) { + return rank; } - - return rank; }), ); } @@ -223,7 +222,7 @@ function maxRank(g) { */ function partition(collection, fn) { var result = { lhs: [], rhs: [] }; - collection.forEach((value) => { + _.forEach(collection, function (value) { if (fn(value)) { result.lhs.push(value); } else { @@ -238,77 +237,14 @@ function partition(collection, fn) { * time it takes to execute the function. */ function time(name, fn) { - var start = Date.now(); + var start = _.now(); try { return fn(); } finally { - console.log(name + ' time: ' + (Date.now() - start) + 'ms'); + console.log(name + ' time: ' + (_.now() - start) + 'ms'); } } function notime(name, fn) { return fn(); } - -let idCounter = 0; -function uniqueId(prefix) { - var id = ++idCounter; - return prefix + id; -} - -/** - * - * @param {number} start - The start of the range. - * @param {number} [limit=null] - The end of the range. If not provided, `start` is used as the limit and the range starts from 0. - * @param {number} [step=1] - The step between each number in the range. Can be negative. - * @returns {number[]} An array of numbers within the specified range. - */ -function range(start, limit = null, step = 1) { - // : number[] - if (limit == null) { - limit = start; - start = 0; - } - - let endCon = (i) => i < limit; - if (step < 0) { - endCon = (i) => limit < i; - } - - const range = []; - for (let i = start; endCon(i); i += step) { - range.push(i); - } - - return range; -} - -function pick(source, keys) { - const dest = {}; - for (const key of keys) { - if (source[key] !== undefined) { - dest[key] = source[key]; - } - } - - return dest; -} - -function mapValues(obj, funcOrProp) { - let func = funcOrProp; - if (typeof funcOrProp === 'string') { - func = (val) => val[funcOrProp]; - } - - return Object.entries(obj).reduce((acc, [k, v]) => { - acc[k] = func(v, k); - return acc; - }, {}); -} - -function zipObject(props, values) { - return props.reduce((acc, key, i) => { - acc[key] = values[i]; - return acc; - }, {}); -} diff --git a/src/dagre/util.test.js b/src/dagre/util.test.js index 118ecc9..a68e9ec 100644 --- a/src/dagre/util.test.js +++ b/src/dagre/util.test.js @@ -240,48 +240,4 @@ describe('util', function () { expect(g.node('b').rank).equals(2); }); }); - - describe('range', () => { - it('Builds an array to the limit', () => { - const range = util.range(4); - expect(range.length).equals(4); - expect(range.reduce((acc, v) => acc + v)).equals(6); - }); - - it('Builds an array with a start', () => { - const range = util.range(2, 4); - expect(range.length).equals(2); - expect(range.reduce((acc, v) => acc + v)).equals(5); - }); - - it('Builds an array with a negative step', () => { - const range = util.range(5, -1, -1); - expect(range[0]).equals(5); - expect(range[5]).equals(0); - }); - }); - - describe('mapValues', () => { - it('Creates an object with the same keys', () => { - const users = { - fred: { user: 'fred', age: 40 }, - pebbles: { user: 'pebbles', age: 1 }, - }; - - const ages = util.mapValues(users, (user) => user.age); // as { fred: number, pebbles: number }; - expect(ages.fred).equals(40); - expect(ages.pebbles).equals(1); - }); - - it('Can take a property name', () => { - const users = { - fred: { user: 'fred', age: 40 }, - pebbles: { user: 'pebbles', age: 1 }, - }; - - const ages = util.mapValues(users, 'age'); // as { fred: number, pebbles: number }; - expect(ages.fred).equals(40); - expect(ages.pebbles).equals(1); - }); - }); }); diff --git a/src/graphlib/alg/components.js b/src/graphlib/alg/components.js index 13bb7fd..d8e1a76 100644 --- a/src/graphlib/alg/components.js +++ b/src/graphlib/alg/components.js @@ -1,3 +1,5 @@ +import * as _ from 'lodash-es'; + export { components }; function components(g) { @@ -9,11 +11,11 @@ function components(g) { if (Object.prototype.hasOwnProperty.call(visited, v)) return; visited[v] = true; cmpt.push(v); - g.successors(v).forEach(dfs); - g.predecessors(v).forEach(dfs); + _.each(g.successors(v), dfs); + _.each(g.predecessors(v), dfs); } - g.nodes().forEach(function (v) { + _.each(g.nodes(), function (v) { cmpt = []; dfs(v); if (cmpt.length) { diff --git a/src/graphlib/alg/dfs.js b/src/graphlib/alg/dfs.js index 03c8477..4278570 100644 --- a/src/graphlib/alg/dfs.js +++ b/src/graphlib/alg/dfs.js @@ -1,3 +1,5 @@ +import * as _ from 'lodash-es'; + export { dfs }; /* @@ -9,58 +11,36 @@ export { dfs }; * Order must be one of "pre" or "post". */ function dfs(g, vs, order) { - if (!Array.isArray(vs)) { + if (!_.isArray(vs)) { vs = [vs]; } var navigation = (g.isDirected() ? g.successors : g.neighbors).bind(g); - var orderFunc = order === 'post' ? postOrderDfs : preOrderDfs; var acc = []; var visited = {}; - vs.forEach((v) => { + _.each(vs, function (v) { if (!g.hasNode(v)) { throw new Error('Graph does not have node: ' + v); } - orderFunc(v, navigation, visited, acc); + doDfs(g, v, order === 'post', visited, navigation, acc); }); return acc; } -function postOrderDfs(v, navigation, visited, acc) { - var stack = [[v, false]]; - while (stack.length > 0) { - var curr = stack.pop(); - if (curr[1]) { - acc.push(curr[0]); - } else { - if (!Object.prototype.hasOwnProperty.call(visited, curr[0])) { - visited[curr[0]] = true; - stack.push([curr[0], true]); - forEachRight(navigation(curr[0]), (w) => stack.push([w, false])); - } - } - } -} +function doDfs(g, v, postorder, visited, navigation, acc) { + if (!Object.prototype.hasOwnProperty.call(visited, v)) { + visited[v] = true; -function preOrderDfs(v, navigation, visited, acc) { - var stack = [v]; - while (stack.length > 0) { - var curr = stack.pop(); - if (!Object.prototype.hasOwnProperty.call(visited, curr)) { - visited[curr] = true; - acc.push(curr); - forEachRight(navigation(curr), (w) => stack.push(w)); + if (!postorder) { + acc.push(v); + } + _.each(navigation(v), function (w) { + doDfs(g, w, postorder, visited, navigation, acc); + }); + if (postorder) { + acc.push(v); } } } - -function forEachRight(array, iteratee) { - var length = array.length; - while (length--) { - iteratee(array[length], length, array); - } - - return array; -} diff --git a/src/graphlib/alg/dijkstra-all.js b/src/graphlib/alg/dijkstra-all.js index 1072760..7363874 100644 --- a/src/graphlib/alg/dijkstra-all.js +++ b/src/graphlib/alg/dijkstra-all.js @@ -1,10 +1,14 @@ +import * as _ from 'lodash-es'; import { dijkstra } from './dijkstra.js'; export { dijkstraAll }; function dijkstraAll(g, weightFunc, edgeFunc) { - return g.nodes().reduce((acc, v) => { - acc[v] = dijkstra(g, v, weightFunc, edgeFunc); - return acc; - }, {}); + return _.transform( + g.nodes(), + function (acc, v) { + acc[v] = dijkstra(g, v, weightFunc, edgeFunc); + }, + {}, + ); } diff --git a/src/graphlib/alg/dijkstra.js b/src/graphlib/alg/dijkstra.js index 44a2fa4..55a70a3 100644 --- a/src/graphlib/alg/dijkstra.js +++ b/src/graphlib/alg/dijkstra.js @@ -1,8 +1,9 @@ +import * as _ from 'lodash-es'; import { PriorityQueue } from '../data/priority-queue.js'; export { dijkstra }; -const DEFAULT_WEIGHT_FUNC = () => 1; +var DEFAULT_WEIGHT_FUNC = _.constant(1); function dijkstra(g, source, weightFn, edgeFn) { return runDijkstra( diff --git a/src/graphlib/alg/find-cycles.js b/src/graphlib/alg/find-cycles.js index 1fe07b3..67fa0d3 100644 --- a/src/graphlib/alg/find-cycles.js +++ b/src/graphlib/alg/find-cycles.js @@ -1,9 +1,10 @@ +import * as _ from 'lodash-es'; import { tarjan } from './tarjan.js'; export { findCycles }; function findCycles(g) { - return tarjan(g).filter(function (cmpt) { + return _.filter(tarjan(g), function (cmpt) { return cmpt.length > 1 || (cmpt.length === 1 && g.hasEdge(cmpt[0], cmpt[0])); }); } diff --git a/src/graphlib/alg/floyd-warshall.js b/src/graphlib/alg/floyd-warshall.js index e0c5ef2..495331e 100644 --- a/src/graphlib/alg/floyd-warshall.js +++ b/src/graphlib/alg/floyd-warshall.js @@ -1,6 +1,8 @@ +import * as _ from 'lodash-es'; + export { floydWarshall }; -const DEFAULT_WEIGHT_FUNC = () => 1; +var DEFAULT_WEIGHT_FUNC = _.constant(1); function floydWarshall(g, weightFn, edgeFn) { return runFloydWarshall( diff --git a/src/graphlib/alg/prim.js b/src/graphlib/alg/prim.js index 314faee..59a7cc5 100644 --- a/src/graphlib/alg/prim.js +++ b/src/graphlib/alg/prim.js @@ -1,3 +1,4 @@ +import * as _ from 'lodash-es'; import { PriorityQueue } from '../data/priority-queue.js'; import { Graph } from '../graph.js'; @@ -25,7 +26,7 @@ function prim(g, weightFunc) { return result; } - g.nodes().forEach((v) => { + _.each(g.nodes(), function (v) { pq.add(v, Number.POSITIVE_INFINITY); result.setNode(v); }); diff --git a/src/graphlib/alg/topsort.js b/src/graphlib/alg/topsort.js index bdda651..1644def 100644 --- a/src/graphlib/alg/topsort.js +++ b/src/graphlib/alg/topsort.js @@ -1,3 +1,5 @@ +import * as _ from 'lodash-es'; + export { topsort, CycleException }; topsort.CycleException = CycleException; @@ -15,15 +17,15 @@ function topsort(g) { if (!Object.prototype.hasOwnProperty.call(visited, node)) { stack[node] = true; visited[node] = true; - g.predecessors(node).forEach(visit); + _.each(g.predecessors(node), visit); delete stack[node]; results.push(node); } } - g.sinks().forEach(visit); + _.each(g.sinks(), visit); - if (Object.keys(visited).length !== g.nodeCount()) { + if (_.size(visited) !== g.nodeCount()) { throw new CycleException(); } diff --git a/src/graphlib/graph.js b/src/graphlib/graph.js index d18b8d7..5eca3f7 100644 --- a/src/graphlib/graph.js +++ b/src/graphlib/graph.js @@ -1,3 +1,5 @@ +import * as _ from 'lodash-es'; + var DEFAULT_EDGE_NAME = '\x00'; var GRAPH_NODE = '\x00'; var EDGE_KEY_DELIM = '\x01'; @@ -22,7 +24,6 @@ var EDGE_KEY_DELIM = '\x01'; // edges up and, object properties, which have string keys, are the closest // we're going to get to a performant hashtable in JavaScript. export class Graph { - // as { directed?: boolean, multigraph?: boolean, compound?: boolean } constructor(opts = {}) { this._isDirected = Object.prototype.hasOwnProperty.call(opts, 'directed') ? opts.directed @@ -38,10 +39,10 @@ export class Graph { this._label = undefined; // Defaults to be set when creating a new node - this._defaultNodeLabelFn = () => undefined; + this._defaultNodeLabelFn = _.constant(undefined); // Defaults to be set when creating a new edge - this._defaultEdgeLabelFn = () => undefined; + this._defaultEdgeLabelFn = _.constant(undefined); // v -> label this._nodes = {}; @@ -92,32 +93,38 @@ export class Graph { } /* === Node functions ========== */ setDefaultNodeLabel(newDefault) { - this._defaultNodeLabelFn = newDefault; - if (typeof newDefault !== 'function') { - this._defaultNodeLabelFn = () => newDefault; + if (!_.isFunction(newDefault)) { + newDefault = _.constant(newDefault); } - + this._defaultNodeLabelFn = newDefault; return this; } nodeCount() { return this._nodeCount; } nodes() { - return Object.keys(this._nodes); + return _.keys(this._nodes); } sources() { - return this.nodes().filter((v) => Object.keys(this._in[v]).length === 0); + var self = this; + return _.filter(this.nodes(), function (v) { + return _.isEmpty(self._in[v]); + }); } sinks() { - return this.nodes().filter((v) => Object.keys(this._out[v]).length === 0); + var self = this; + return _.filter(this.nodes(), function (v) { + return _.isEmpty(self._out[v]); + }); } setNodes(vs, value) { var args = arguments; - vs.forEach((v) => { + var self = this; + _.each(vs, function (v) { if (args.length > 1) { - this.setNode(v, value); + self.setNode(v, value); } else { - this.setNode(v); + self.setNode(v); } }); return this; @@ -157,15 +164,15 @@ export class Graph { if (this._isCompound) { this._removeFromParentsChildList(v); delete this._parent[v]; - this.children(v).forEach((child) => { + _.each(this.children(v), (child) => { this.setParent(child); }); delete this._children[v]; } - Object.keys(this._in[v]).forEach(removeEdge); + _.each(_.keys(this._in[v]), removeEdge); delete this._in[v]; delete this._preds[v]; - Object.keys(this._out[v]).forEach(removeEdge); + _.each(_.keys(this._out[v]), removeEdge); delete this._out[v]; delete this._sucs[v]; --this._nodeCount; @@ -177,12 +184,12 @@ export class Graph { throw new Error('Cannot set parent in a non-compound graph'); } - if (parent === undefined) { + if (_.isUndefined(parent)) { parent = GRAPH_NODE; } else { // Coerce parent to string parent += ''; - for (var ancestor = parent; ancestor !== undefined; ancestor = this.parent(ancestor)) { + for (var ancestor = parent; !_.isUndefined(ancestor); ancestor = this.parent(ancestor)) { if (ancestor === v) { throw new Error('Setting ' + parent + ' as parent of ' + v + ' would create a cycle'); } @@ -208,11 +215,15 @@ export class Graph { } } } - children(v = GRAPH_NODE) { + children(v) { + if (_.isUndefined(v)) { + v = GRAPH_NODE; + } + if (this._isCompound) { var children = this._children[v]; if (children) { - return Object.keys(children); + return _.keys(children); } } else if (v === GRAPH_NODE) { return this.nodes(); @@ -223,24 +234,19 @@ export class Graph { predecessors(v) { var predsV = this._preds[v]; if (predsV) { - return Object.keys(predsV); + return _.keys(predsV); } } successors(v) { var sucsV = this._sucs[v]; if (sucsV) { - return Object.keys(sucsV); + return _.keys(sucsV); } } neighbors(v) { var preds = this.predecessors(v); if (preds) { - const union = new Set(preds); - for (var succ of this.successors(v)) { - union.add(succ); - } - - return Array.from(union.values()); + return _.union(preds, this.successors(v)); } } isLeaf(v) { @@ -263,14 +269,14 @@ export class Graph { copy.setGraph(this.graph()); var self = this; - Object.entries(this._nodes).forEach(function ([v, value]) { + _.each(this._nodes, function (value, v) { if (filter(v)) { copy.setNode(v, value); } }); - // Object.values(this._edgeObjs).forEach((e: { v: string, w: string }) => { - Object.values(this._edgeObjs).forEach((e) => { + _.each(this._edgeObjs, function (e) { + // @ts-expect-error if (copy.hasNode(e.v) && copy.hasNode(e.w)) { copy.setEdge(e, self.edge(e)); } @@ -290,45 +296,44 @@ export class Graph { } if (this._isCompound) { - copy.nodes().forEach((v) => copy.setParent(v, findParent(v))); + _.each(copy.nodes(), function (v) { + copy.setParent(v, findParent(v)); + }); } return copy; } /* === Edge functions ========== */ setDefaultEdgeLabel(newDefault) { - this._defaultEdgeLabelFn = newDefault; - if (typeof newDefault !== 'function') { - this._defaultEdgeLabelFn = () => newDefault; + if (!_.isFunction(newDefault)) { + newDefault = _.constant(newDefault); } - + this._defaultEdgeLabelFn = newDefault; return this; } edgeCount() { return this._edgeCount; } edges() { - return Object.values(this._edgeObjs); + return _.values(this._edgeObjs); } setPath(vs, value) { - const args = arguments; - if (vs.length > 0) { - vs.reduce((v, w) => { - if (args.length > 1) { - this.setEdge(v, w, value); - } else { - this.setEdge(v, w); - } - return w; - }); - } + var self = this; + var args = arguments; + _.reduce(vs, function (v, w) { + if (args.length > 1) { + self.setEdge(v, w, value); + } else { + self.setEdge(v, w); + } + return w; + }); return this; } /* * setEdge(v, w, [value, [name]]) * setEdge({ v, w, [name] }, [value]) */ - // setEdge(u1, u2, u3, u4) { setEdge() { var v, w, name, value; var valueSpecified = false; @@ -354,7 +359,7 @@ export class Graph { v = '' + v; w = '' + w; - if (name !== undefined) { + if (!_.isUndefined(name)) { name = '' + name; } @@ -366,7 +371,7 @@ export class Graph { return this; } - if (name !== undefined && !this._isMultigraph) { + if (!_.isUndefined(name) && !this._isMultigraph) { throw new Error('Cannot set a named edge when isMultigraph = false'); } @@ -393,7 +398,7 @@ export class Graph { return this; } edge(v, w, name) { - const e = + var e = arguments.length === 1 ? edgeObjToId(this._isDirected, arguments[0]) : edgeArgsToId(this._isDirected, v, w, name); @@ -428,21 +433,25 @@ export class Graph { inEdges(v, u) { var inV = this._in[v]; if (inV) { - var edges = Object.values(inV); + var edges = _.values(inV); if (!u) { return edges; } - return edges.filter((edge) => edge.v === u); + return _.filter(edges, function (edge) { + return edge.v === u; + }); } } outEdges(v, w) { var outV = this._out[v]; if (outV) { - var edges = Object.values(outV); + var edges = _.values(outV); if (!w) { return edges; } - return edges.filter((edge) => edge.w === w); + return _.filter(edges, function (edge) { + return edge.w === w; + }); } } nodeEdges(v, w) { @@ -481,19 +490,18 @@ function edgeArgsToId(isDirected, v_, w_, name) { v = w; w = tmp; } - return v + EDGE_KEY_DELIM + w + EDGE_KEY_DELIM + (name === undefined ? DEFAULT_EDGE_NAME : name); + return v + EDGE_KEY_DELIM + w + EDGE_KEY_DELIM + (_.isUndefined(name) ? DEFAULT_EDGE_NAME : name); } function edgeArgsToObj(isDirected, v_, w_, name) { - let v = '' + v_; - let w = '' + w_; + var v = '' + v_; + var w = '' + w_; if (!isDirected && v > w) { var tmp = v; v = w; w = tmp; } - // var edgeObj = { v, w } as { v: string, w: string, name?: string }; - var edgeObj = { v, w }; + var edgeObj = { v: v, w: w }; if (name) { edgeObj.name = name; } diff --git a/src/graphlib/json.js b/src/graphlib/json.js index 1e2f2c2..b76b07c 100644 --- a/src/graphlib/json.js +++ b/src/graphlib/json.js @@ -1,9 +1,10 @@ +import * as _ from 'lodash-es'; import { Graph } from './graph.js'; export { write, read }; function write(g) { - const json = { + var json = { options: { directed: g.isDirected(), multigraph: g.isMultigraph(), @@ -11,24 +12,22 @@ function write(g) { }, nodes: writeNodes(g), edges: writeEdges(g), - value: undefined, }; - if (g.graph() !== undefined) { - json.value = structuredClone(g.graph()); + if (!_.isUndefined(g.graph())) { + json.value = _.clone(g.graph()); } return json; } function writeNodes(g) { - return g.nodes().map((v) => { - const nodeValue = g.node(v); - const parent = g.parent(v); - // const node = { v } as { v: string; name?: string; value?: any; parent?: string }; - const node = { v }; - if (nodeValue !== undefined) { + return _.map(g.nodes(), function (v) { + var nodeValue = g.node(v); + var parent = g.parent(v); + var node = { v: v }; + if (!_.isUndefined(nodeValue)) { node.value = nodeValue; } - if (parent !== undefined) { + if (!_.isUndefined(parent)) { node.parent = parent; } return node; @@ -36,13 +35,13 @@ function writeNodes(g) { } function writeEdges(g) { - return g.edges().map((e) => { + return _.map(g.edges(), function (e) { var edgeValue = g.edge(e); - var edge = { v: e.v, w: e.w }; // as { v: string; w: string; name?: string; value?: any } - if (e.name !== undefined) { + var edge = { v: e.v, w: e.w }; + if (!_.isUndefined(e.name)) { edge.name = e.name; } - if (edgeValue !== undefined) { + if (!_.isUndefined(edgeValue)) { edge.value = edgeValue; } return edge; @@ -51,13 +50,13 @@ function writeEdges(g) { function read(json) { var g = new Graph(json.options).setGraph(json.value); - json.nodes.forEach((entry) => { + _.each(json.nodes, function (entry) { g.setNode(entry.v, entry.value); if (entry.parent) { g.setParent(entry.v, entry.parent); } }); - json.edges.forEach((entry) => { + _.each(json.edges, function (entry) { g.setEdge({ v: entry.v, w: entry.w, name: entry.name }, entry.value); }); return g;