UNPKG

jsnetworkx

Version:

A graph processing and visualization library for JavaScript (port of NetworkX for Python).

1,316 lines (1,181 loc) 38.3 kB
'use strict'; var _get = require('babel-runtime/helpers/get')['default']; var _inherits = require('babel-runtime/helpers/inherits')['default']; var _createClass = require('babel-runtime/helpers/create-class')['default']; var _classCallCheck = require('babel-runtime/helpers/class-call-check')['default']; var _slicedToArray = require('babel-runtime/helpers/sliced-to-array')['default']; var _Object$keys = require('babel-runtime/core-js/object/keys')['default']; var _Object$assign = require('babel-runtime/core-js/object/assign')['default']; var _Object$create = require('babel-runtime/core-js/object/create')['default']; var _Array$from = require('babel-runtime/core-js/array/from')['default']; var _regeneratorRuntime = require('babel-runtime/regenerator')['default']; var _getIterator = require('babel-runtime/core-js/get-iterator')['default']; var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default']; Object.defineProperty(exports, '__esModule', { value: true }); var _Graph2 = require('./Graph'); var _Graph3 = _interopRequireDefault(_Graph2); var _exceptionsJSNetworkXError = require('../exceptions/JSNetworkXError'); var _exceptionsJSNetworkXError2 = _interopRequireDefault(_exceptionsJSNetworkXError); var _internals = require('../_internals'); /** * An undirected graph class that can store multiedges. * * Multiedges are multiple edges between two nodes. Each edge * can hold optional data or attributes. A MultiGraph holds undirected edges. * Self loops are allowed. * * Edges are represented as links between nodes with optional * key/value attributes. * * ### Examples * * Create an empty graph structure (a "null graph") with no nodes and no edges. * * ``` * var G = jsnx.MultiGraph(); * ``` * * G can be grown in several ways. * * #### Nodes * * Add one node at a time: * * ``` * G.addNode(1); * ``` * * Add the nodes from any iterable: * * ``` * G.addNodesFrom([2, 3]); * var H = jsnx.Graph(); * H.addPath([0,1,2,3,4,5,6,7,8,9]); * G.addNodesFrom(h); * ``` * * In addition to strings and integers, any object that implements a custom * `toString` method can be used as node. For example, arrays: * * ``` * G.addNode([1,2]); * ``` * * #### Edges * * A graph can also be grown by adding edges. * * Add one edge, * * ``` * G.addEdge(1, 2); * ``` * * a list or collection of edges, * * ``` * G.addEdgesFrom([[1,2], [1,3]]); * G.addEdgesFrom(H.edges()); * ``` * * If some edges connect nodes not yet in the graph, the nodes are added * automatically. If an edge already exists, an addition edge is created and * stored using a key to identify the edge. By default, the key is the lowest * unused integer. * * ``` * G.addEdgesFrom([[4,5,{route: 282}], [4,5,{route: 37}]]); * G.get(4); * // Map { 3: {0: {}}, 5: {0: {}, 1: {route: 282}, 2: {route: 37}}} * ``` * * #### Attributes * * Each graph, node and edge can hold key/value attribute pairs in an associated * attribute "dictionary" (object). By defauly these are empty, but can be added * or changed using `addEdge` or `addNode`. * * ``` * var G = jsnx.MultiGraph(null, {day: Friday}): * G.graph * // {day: 'Friday'} * * G.addNode(1, {time: '5pm'}); * G.addNodesFrom([3], {time: '2pm'}); * G.nodes(true); * // [[1, {time: '5pm'}], [3, {time: '2pm'}]] * ``` * * @see Graph * @see DiGraph * @see MultiDiGraph * */ var MultiGraph = (function (_Graph) { _inherits(MultiGraph, _Graph); /** * @param {?} optData Data to initialze graph. * If no data is provided, an empty graph is created. The data can be * an edge list or any graph object. * @param {Object=} optAttr Attributes to add to graph as key=value pairs. */ function MultiGraph(optData, optAttr) { _classCallCheck(this, MultiGraph); _get(Object.getPrototypeOf(MultiGraph.prototype), 'constructor', this).call(this, optData, optAttr); } _createClass(MultiGraph, [{ key: 'addEdge', /** * Add an edge between u and v. * * The nodes u and v will be automatically added if they are * not already in the graph. * * Edge attributes can be specified with keywords or by providing * a dictionary with key/value pairs. * * ### Notes: * * To replace/update edge data, use the optional key argument * to identify a unique edge. Otherwise a new edge will be created. * * NetworkX algorithms designed for weighted graphs cannot use * multigraphs directly because it is not clear how to handle * multiedge weights. Convert to Graph using edge attribute * 'weight' to enable weighted graph algorithms. * * ### Example * * The following all add the edge [1,2] to the graph G: * * ``` * var G = jsnx.MultiGraph(); * var e = [1,2]; * G.addEdge(1, 2); * G.addEdge.apply(G, e); * G.addEdgesFrom([e]); * ``` * Associate data to edges by passing a data object: * * ``` * G.addEdge(1, 2, {weight: 3}); * G.addEdge(1, 2, 0, {weight: 4}); // update data for key=0 * G.addEdge(1, 3, {weight: 7, capacity: 15, length: 342.7}); * ``` * @see #addEdgesFrom * * @param {Node} u node * @param {Node} v node * @param {?(number|string)=} optKey identifier * Used to distinguish multiedges between a pair of nodes. Default is * the lowest unused integer. * @param {?Object=} optAttrDict Dictionary of edge attributes. * Key/value pairs will update existing data associated with the edge. */ value: function addEdge(u, v, optKey, optAttrDict) { var type = typeof optKey; if (optKey != null && type !== 'number' && type !== 'string') { optAttrDict = optKey; optKey = null; } // set up attribute dict if (optAttrDict && !(0, _internals.isPlainObject)(optAttrDict)) { throw new _exceptionsJSNetworkXError2['default']('The optAttrDict argument must be an object.'); } // add nodes if (!this.adj.has(u)) { this.adj.set(u, new _internals.Map()); this.node.set(u, {}); } if (!this.adj.has(v)) { this.adj.set(v, new _internals.Map()); this.node.set(v, {}); } var keydict; if (this.adj.get(u).has(v)) { keydict = this.adj.get(u).get(v); if (optKey == null) { // find a unique integer key // other methods might be better here? optKey = _Object$keys(keydict).length; while (keydict[optKey]) { // ok, because values are objects only optKey += 1; } } var datadict = keydict[optKey] || {}; keydict[optKey] = _Object$assign(datadict, optAttrDict); } else { // selfloops work this way without special treatment if (optKey == null) { optKey = 0; } keydict = _Object$create(null); keydict[optKey] = _Object$assign({}, optAttrDict); this.adj.get(u).set(v, keydict); this.adj.get(v).set(u, keydict); } } }, { key: 'addEdgesFrom', /** * Add all the edges in `ebunch`. * * Adding the same edge twice has no effect but any edge data will be updated * when each duplicate edge is added. * * Edge attributes specified in edges as a tuple take precedence over the * attributes specified generally. * * ### Example * * ``` * var G = new jsnx.MultiGraph(); * G.addEdgesFrom([[0,1], [1,2]]); * ``` * * Associate data to edges * * ``` * G.addEdgesFrom([[1,2], [2,3]], {weight: 3}); * G.addEdgesFrom([[1,2], [2,3]], {label: 'WN2898'}); * ``` * * @see #addEdge * @see #addWeightedEdgesFrom * * * @param {Iterable} ebunch container of edges * Each edge given in the container will be added to the * graph. The edges can be: * * - 2-tuples (u,v) or * - 3-tuples (u,v,d) for an edge attribute dict d or * - 4-tuples (u,v,k,d) for an edge identified by key k * * @param {Object=} optAttrDict Dictionary of edge attributes. * Key/value pairs will update existing data associated with each edge. */ value: function addEdgesFrom(ebunch, optAttrDict) { // istanbul ignore next var _this = this; if (optAttrDict && !(0, _internals.isPlainObject)(optAttrDict)) { throw new _exceptionsJSNetworkXError2['default']('The optAttrDict argument must be an object.'); } // process ebunch (0, _internals.forEach)(ebunch, function (edge) { var u; var v; var key; var data; switch (edge.length) { case 4: u = edge[0]; v = edge[1]; key = edge[2]; data = edge[3]; break; case 3: u = edge[0]; v = edge[1]; data = edge[2]; break; case 2: u = edge[0]; v = edge[1]; break; default: if (!(0, _internals.isArrayLike)(edge)) { throw new TypeError('Elements in edgelists must be tuples.'); } throw new _exceptionsJSNetworkXError2['default']((0, _internals.sprintf)('Edge tuple %j must be a 2-tuple, 3-tuple or 4-tuple.', edge)); } var keydict = _this.adj.has(u) ? _this.adj.get(u).get(v) || _Object$create(null) : _Object$create(null); if (key == null) { // find a unique integer key // other methods might be better here? key = _Object$keys(keydict).length; while (keydict[key]) { key += 1; } } var datadict = keydict[key] || {}; _Object$assign(datadict, optAttrDict, data); _this.addEdge(u, v, key, datadict); }); } }, { key: 'removeEdge', /** * Remove an edge between u and v. * * ### Example * * ``` * var G = new jsnx.MultiGraph(); * G.addPath([0,1,2,3]); * G.removeEdge(0, 1); * ``` * * For multiple edges * * ``` * var G = new jsnx.MultiGraph(); * G.addEdgesFrom([[1,2], [1,2], [1,2]]); * G.removeEdge(1, 2); // remove a single edge * ``` * * For edges with keys * * ``` * var G = new jsnx.MultiGraph(); * G.addEdge(1, 2, 'first'); * G.addEdge(1, 2, 'second'); * G.removeEdge(1, 2, 'second'); * ``` * * @see #removeEdgesFrom * * @param {Node} u * @param {Node} v * @param {(number|string)=} optKey * Used to distinguish multiple edges between a pair of nodes. * If null or undefined remove a single (arbitrary) edge between u and v. */ value: function removeEdge(u, v, optKey) { var keydict; var neightborsOfU = this.adj.get(u); if (neightborsOfU) { keydict = neightborsOfU.get(v); } if (keydict == null) { throw new _exceptionsJSNetworkXError2['default']((0, _internals.sprintf)('The edge %j-%j is not in the graph', u, v)); } // remove the edge with specified data if (optKey == null) { for (var key in keydict) { delete keydict[key]; break; } } else { if (!keydict[optKey]) { throw new _exceptionsJSNetworkXError2['default']((0, _internals.sprintf)('The edge %j-%j with key %j is not in the graph', u, v, optKey)); } delete keydict[optKey]; } if (_Object$keys(keydict).length === 0) { // remove the key entries if last edge neightborsOfU['delete'](v); if (!(0, _internals.nodesAreEqual)(u, v)) { this.adj.get(v)['delete'](u); } } } }, { key: 'removeEdgesFrom', /** * Remove all edges specified in `ebunch`. * * Will fail silently if an edge in `ebunch` is not in the graph. * * ### Example * * ``` * var G = new jsnx.MultiGraph(); * G.addPath([0,1,2,3]); * var ebunch = [[1,2], [2,3]]; * G.removeEdgesFrom(ebunch); * ``` * * Removing multiple copies of edges. * * ``` * var G = new jsnx.MultiGraph(); * G.addEdgesFrom([[1,2], [1,2], [1,2]]); * G.removeEdgesFrom([[1,2], [1,2]]); * G.edges(); * // [[1,2]] * ``` * * @see #removeEdge * * @param {?} ebunch list or container of edge tuples * Each edge given in the list or container will be removed * from the graph. The edges can be: * * - 2-tuples (u,v) All edges between u and v are removed. * - 3-tuples (u,v,key) The edge identified by key is removed. */ value: function removeEdgesFrom(ebunch) { // istanbul ignore next var _this2 = this; (0, _internals.forEach)(ebunch, function (edge) { try { _this2.removeEdge(edge[0], edge[1], edge[2]); } catch (ex) { if (!(ex instanceof _exceptionsJSNetworkXError2['default'])) { throw ex; } } }); } }, { key: 'hasEdge', /** * Return True if the graph has an edge between nodes u and v. * * ### Example * * ``` * var G = new jsnx.MultiGraph(); * G.addPath([0,1,2,3]); * G.hasEdge(0,1); * // true * G.addEdge(0, 1, 'a'); * G.hasEdge(0, 1, 'a'); * // true * ``` * * The following syntax are equivalent: * * ``` * G.hasEdge(0, 1); * // true * G.get(0).has(1); * // true * ``` * * @param {Node} u node * @param {Node} v node * @param {(string|number)=} optKey If specified return true only * if the edge with key is found. * * @return {boolean} true if edge is in the graph, false otherwise. */ value: function hasEdge(u, v, optKey) { var neighborsOfU = this.adj.get(u); if (neighborsOfU) { return neighborsOfU.has(v) && (optKey == null || !!neighborsOfU.get(v)[optKey]); } return false; } }, { key: 'edges', /** * Return a list of edges. * * Edges are returned as tuples with optional data and keys in the order * (node, neighbor, key, data). * * Nodes in `nbunch` that are not in the graph will be (quietly) ignored. * * ### Example * * ``` * var G = new jsnx.MultiGraph(); * G.addPath([0,1,2,3]); * G.edges(); * // [[0,1], [1,2], [2,3]] * G.edges(true); * // [[0,1,{}], [1,2,{}], [2,3,{}]] * G.edges(false, true); * // [[0,1,0], [1,2,0], [2,3,0]] * G.edges(true, true); * // [[0,1,0,{}], [1,2,0,{}], [2,3,0,{}]] * G.edges([0,3]); * // [[0,1], [3, 2]] * G.edges(0); * // [[0,1]] * ``` * * @see #edgesIter * * @param {?NodeContainer=} optNbunch A container of nodes. * The container will be iterated through once. * @param {?boolean=} optData (default=False) * Return two tuples (u,v) (False) or three-tuples (u,v,data) (True). * @param {?boolean=} optKeys (default=False) * Return two tuples (u,v) (False) or three-tuples (u,v,key) (True). * * @return {!Array} list of edge tuples * Edges that are adjacent to any node in nbunch, or a list * of all edges if nbunch is not specified. */ value: function edges(optNbunch, optData, optKeys) { return _Array$from(this.edgesIter(optNbunch, optData, optKeys)); } }, { key: 'edgesIter', /** * Return an iterator over edges. * * Edges are returned as tuples with optional data and keys * in the order (node, neighbor, key, data). * * Nodes in nbunch that are not in the graph will be (quietly) ignored. * * ### Example * * ``` * var G = new jsnx.MultiGraph(); * G.addPath([0,1,2,3]); * Array.from(G.edgesIter); * // [[0,1], [1,2], [2,3]] * Array.from(G.edges(true)); * // [[0,1,{}], [1,2,{}], [2,3,{}]] * Array.from(G.edges(false, true)); * // [[0,1,0], [1,2,0], [2,3,0]] * Array.from(G.edges(true, true)); * // [[0,1,0,{}], [1,2,0,{}], [2,3,0,{}]] * Array.from(G.edges([0,3])); * // [[0,1], [3, 2]] * Array.from(G.edges(0)); * // [[0,1]] * ``` * * @see #edges * * @param {?(NodeContainer|boolean)=} optNbunch A container of nodes. * The container will be iterated through once. * @param {?boolean=} optData (default=False) * If True, return edge attribute dict with each edge. * @param {?boolean=} optKeys (default=False) * If True, return edge keys with each edge. * * @return {!Iterator} * An iterator of (u,v), (u,v,d) or (u,v,key,d) tuples of edges. * * @override * @export */ value: _regeneratorRuntime.mark(function edgesIter(optNbunch) { var optData = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; var optKeys = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; var seen, nodesNbrs, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, _step$value, n, nbrs, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, _step2$value, nbr, keydict, key, tuple; return _regeneratorRuntime.wrap(function edgesIter$(context$2$0) { // istanbul ignore next var _this3 = this; while (1) switch (context$2$0.prev = context$2$0.next) { case 0: if (typeof optNbunch === 'boolean') { optKeys = optData; optData = optNbunch; optNbunch = null; } seen = new _internals.Set(); nodesNbrs = optNbunch == null ? this.adj : (0, _internals.mapIterator)(this.nbunchIter(optNbunch), function (n) { return (0, _internals.tuple2)(n, _this3.adj.get(n)); }); _iteratorNormalCompletion = true; _didIteratorError = false; _iteratorError = undefined; context$2$0.prev = 6; _iterator = _getIterator(nodesNbrs); case 8: if (_iteratorNormalCompletion = (_step = _iterator.next()).done) { context$2$0.next = 53; break; } _step$value = _slicedToArray(_step.value, 2); n = _step$value[0]; nbrs = _step$value[1]; _iteratorNormalCompletion2 = true; _didIteratorError2 = false; _iteratorError2 = undefined; context$2$0.prev = 15; _iterator2 = _getIterator(nbrs); case 17: if (_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done) { context$2$0.next = 36; break; } _step2$value = _slicedToArray(_step2.value, 2); nbr = _step2$value[0]; keydict = _step2$value[1]; if (seen.has(nbr)) { context$2$0.next = 33; break; } context$2$0.t0 = _regeneratorRuntime.keys(keydict); case 23: if ((context$2$0.t1 = context$2$0.t0()).done) { context$2$0.next = 32; break; } key = context$2$0.t1.value; tuple = [n, nbr]; if (optKeys) { tuple[2] = key; } if (optData) { tuple.push(keydict[key]); } context$2$0.next = 30; return tuple; case 30: context$2$0.next = 23; break; case 32: seen.add(n); case 33: _iteratorNormalCompletion2 = true; context$2$0.next = 17; break; case 36: context$2$0.next = 42; break; case 38: context$2$0.prev = 38; context$2$0.t2 = context$2$0['catch'](15); _didIteratorError2 = true; _iteratorError2 = context$2$0.t2; case 42: context$2$0.prev = 42; context$2$0.prev = 43; if (!_iteratorNormalCompletion2 && _iterator2['return']) { _iterator2['return'](); } case 45: context$2$0.prev = 45; if (!_didIteratorError2) { context$2$0.next = 48; break; } throw _iteratorError2; case 48: return context$2$0.finish(45); case 49: return context$2$0.finish(42); case 50: _iteratorNormalCompletion = true; context$2$0.next = 8; break; case 53: context$2$0.next = 59; break; case 55: context$2$0.prev = 55; context$2$0.t3 = context$2$0['catch'](6); _didIteratorError = true; _iteratorError = context$2$0.t3; case 59: context$2$0.prev = 59; context$2$0.prev = 60; if (!_iteratorNormalCompletion && _iterator['return']) { _iterator['return'](); } case 62: context$2$0.prev = 62; if (!_didIteratorError) { context$2$0.next = 65; break; } throw _iteratorError; case 65: return context$2$0.finish(62); case 66: return context$2$0.finish(59); case 67: case 'end': return context$2$0.stop(); } }, edgesIter, this, [[6, 55, 59, 67], [15, 38, 42, 50], [43,, 45, 49], [60,, 62, 66]]); }) }, { key: 'getEdgeData', /** * Return the attribute dictionary associated with edge (u,v). * * ### Example * * ``` * var G = jsnx.MultiGraph(); * G.addPath([0,1,2,3]); * G.getEdgeData(0, 1); * // {0: {}} * G.getEdgeData('a', 'b', null, 0); // edge not in graph, return 0 * // 0 * ``` * * @param {Node} u node * @param {Node} v node * @param {(string|number)=} optKey Return data only for the edge with * specified key. * @param {T=} optDefault Value to return if the edge (u,v) is not found. * * @return {(Object|T)} The edge attribute dictionary. * @template T */ value: function getEdgeData(u, v, optKey, optDefault) { var neightborsOfU = this.adj.get(u); if (neightborsOfU) { if (optKey == null) { return neightborsOfU.get(v) || optDefault; } return neightborsOfU.has(v) && neightborsOfU.get(v)[optKey] || optDefault; } } }, { key: 'degreeIter', /** * Return an iterator for (node, degree). * * The node degree is the number of edges adjacent to the node. * * ### Example * * ``` * var G = new jsnx.Graph(); * G.addPath([0,1,2,3]); * Array.from(G.degreeIter(0)); * // [[0,1]] // node 0 with degree 1 * Array.from(G.degreeIter([0,1])); * // [[0,1], [1,2]] * * @see #degree * * @param {?(Node|NodeContainer)=} optNbunch A container of nodes * The container will be iterated through once. * @param {?string=} optWeight The edge attribute that holds the numerical * value used as a weight. If undefined, then each edge has weight 1. * The degree is the sum of the edge weights adjacent to the node. * * @return {!Iterator} The iterator returns two-tuples of (node, degree). */ value: _regeneratorRuntime.mark(function degreeIter(optNbunch, optWeight) { var nodesNbrs, _iteratorNormalCompletion3, _didIteratorError3, _iteratorError3, _iterator3, _step3, _step3$value, n, nbrs, deg, keydict, key; return _regeneratorRuntime.wrap(function degreeIter$(context$2$0) { // istanbul ignore next var _this4 = this; while (1) switch (context$2$0.prev = context$2$0.next) { case 0: if (typeof optNbunch === 'string') { optWeight = optNbunch; optNbunch = null; } nodesNbrs = optNbunch == null ? this.adj : (0, _internals.mapIterator)(this.nbunchIter(optNbunch), function (n) { return (0, _internals.tuple2)(n, _this4.adj.get(n)); }); _iteratorNormalCompletion3 = true; _didIteratorError3 = false; _iteratorError3 = undefined; context$2$0.prev = 5; _iterator3 = _getIterator(nodesNbrs); case 7: if (_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done) { context$2$0.next = 25; break; } _step3$value = _slicedToArray(_step3.value, 2); n = _step3$value[0]; nbrs = _step3$value[1]; deg = 0; if (!(optWeight == null)) { context$2$0.next = 18; break; } nbrs.forEach(function (keydict) { return deg += _Object$keys(keydict).length; }); context$2$0.next = 16; return [n, deg + +(nbrs.has(n) && _Object$keys(nbrs.get(n)).length)]; case 16: context$2$0.next = 22; break; case 18: // edge weighted graph - degree is sum of nbr edge weights nbrs.forEach(function (keydict) { for (var key in keydict) { deg += (0, _internals.getDefault)(keydict[key][optWeight], 1); } }); if (nbrs.has(n)) { keydict = nbrs.get(n); for (key in keydict) { deg += (0, _internals.getDefault)(keydict[key][optWeight], 1); } } context$2$0.next = 22; return [n, deg]; case 22: _iteratorNormalCompletion3 = true; context$2$0.next = 7; break; case 25: context$2$0.next = 31; break; case 27: context$2$0.prev = 27; context$2$0.t0 = context$2$0['catch'](5); _didIteratorError3 = true; _iteratorError3 = context$2$0.t0; case 31: context$2$0.prev = 31; context$2$0.prev = 32; if (!_iteratorNormalCompletion3 && _iterator3['return']) { _iterator3['return'](); } case 34: context$2$0.prev = 34; if (!_didIteratorError3) { context$2$0.next = 37; break; } throw _iteratorError3; case 37: return context$2$0.finish(34); case 38: return context$2$0.finish(31); case 39: case 'end': return context$2$0.stop(); } }, degreeIter, this, [[5, 27, 31, 39], [32,, 34, 38]]); }) }, { key: 'isMultigraph', /** * Return true if graph is a multigraph, false otherwise. * * @return {boolean} true if graph is a multigraph, false otherwise. */ value: function isMultigraph() { return true; } }, { key: 'isDirected', /** * Return true if graph is directed, false otherwise. * * @return {boolean} True if graph is directed, False otherwise. */ value: function isDirected() { return false; } }, { key: 'toDirected', /** * Return a directed representation of the graph. * * ### Notes * * This returns a "deepcopy" of the edge, node, and graph attributes which * attempts to completely copy all of the data and references. * * This is in contrast to the similar D = DiGraph(G) which returns a shallow * copy of the data. * * ### Example * * ``` * var G = new jsnx.MultiGraph(); * G.addPath([0,1]); * var H = G.toDirected(); * G.edges(); * // [[0,1], [1,0]] * ``` * * If already directed, return a (deep) copy * * ``` * var G = new jsnx.MultiDiGraph(); * G.addPath([0,1]); * var H = G.toDirected(); * G.edges(); * // [[0,1]] * ``` * * @return {!MultiDiGraph} * A directed graph with the same name, same nodes, and with * each edge (u,v,data) replaced by two directed edges * (u,v,data) and (v,u,data). */ value: function toDirected() { var G = new (require('./MultiDiGraph'))(); G.addNodesFrom(this); var _iteratorNormalCompletion4 = true; var _didIteratorError4 = false; var _iteratorError4 = undefined; try { for (var _iterator4 = _getIterator(this.adjacencyIter()), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { var _step4$value = _slicedToArray(_step4.value, 2); var u = _step4$value[0]; var nbrs = _step4$value[1]; var _iteratorNormalCompletion5 = true; var _didIteratorError5 = false; var _iteratorError5 = undefined; try { for (var _iterator5 = _getIterator(nbrs), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { var _step5$value = _slicedToArray(_step5.value, 2); var v = _step5$value[0]; var keydict = _step5$value[1]; for (var key in keydict) { G.addEdge(u, v, key, (0, _internals.deepcopy)(keydict[key])); } } } catch (err) { _didIteratorError5 = true; _iteratorError5 = err; } finally { try { if (!_iteratorNormalCompletion5 && _iterator5['return']) { _iterator5['return'](); } } finally { if (_didIteratorError5) { throw _iteratorError5; } } } } } catch (err) { _didIteratorError4 = true; _iteratorError4 = err; } finally { try { if (!_iteratorNormalCompletion4 && _iterator4['return']) { _iterator4['return'](); } } finally { if (_didIteratorError4) { throw _iteratorError4; } } } G.graph = (0, _internals.deepcopy)(this.graph); G.node = (0, _internals.deepcopy)(this.node); return G; } }, { key: 'selfloopEdges', /** * Return a list of selfloop edges. * * ### Example * * ``` * var G = new jsnx.MultiGraph(); * G.addEdge(1, 1); * G.addEdge(1, 2); * G.selfloopEdges(); * // [[1,1]] * G.selfloopEdges(true); * // [[1,1,{}]] * G.selfloopEdges(false, true); * // [[1,1,0]] * G.selfloopEdges(true, true); * // [[1,1,0,{}]] * ``` * * @see #nodesWithSelfloops * @see #numberOfSelfloops * * * @param {boolean=} optData (default=False) * Return selfloop edges as two tuples (u,v) (data=False) * or three-tuples (u,v,data) (data=True) * @param {boolean=} optKeys (default=False) * If True, return edge keys with each edge * * @return {Array} A list of all selfloop edges */ value: function selfloopEdges() { var optData = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0]; var optKeys = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; var edges = []; var _iteratorNormalCompletion6 = true; var _didIteratorError6 = false; var _iteratorError6 = undefined; try { for (var _iterator6 = _getIterator(this.adj), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { var _step6$value = _slicedToArray(_step6.value, 2); var n = _step6$value[0]; var nbrs = _step6$value[1]; if (nbrs.has(n)) { var keydict = nbrs.get(n); for (var key in keydict) { var edge = [n, n]; if (optKeys) { edge[2] = key; } if (optData) { edge.push(keydict[key]); } edges.push(edge); } } } } catch (err) { _didIteratorError6 = true; _iteratorError6 = err; } finally { try { if (!_iteratorNormalCompletion6 && _iterator6['return']) { _iterator6['return'](); } } finally { if (_didIteratorError6) { throw _iteratorError6; } } } return edges; } }, { key: 'numberOfEdges', /** * Return the number of edges between two nodes. * * ### Example * * ``` * var G = new jsnx.MultiGraph(); * G.addPath([0,1,2,3]); * G.numberOfEdges(); * // 3 * G.numberOfEdges(0,1); * // 1 * ``` * * @see #size * * @param {Node=} optU node * @param {Node=} optV node * If u and v are specified, return the number of edges between * u and v. Otherwise return the total number of all edges. * * @return {number} The number of edges in the graph. * If nodes u and v are specified return the number of edges between * those nodes. */ value: function numberOfEdges(optU, optV) { if (optU == null || optV == null) { return this.size(); } var neightborsOfU = this.get(optU); if (neightborsOfU) { return neightborsOfU.has(optV) ? _Object$keys(neightborsOfU.get(optV)).length : 0; } return 0; } }, { key: 'subgraph', /** * Return the subgraph induced on nodes in nbunch. * * The induced subgraph of the graph contains the nodes in nbunch and the * edges between those nodes. * * ### Notes * * The graph, edge or node attributes just point to the original graph. * So changes to the node or edge structure will not be reflected in * the original graph while changes to the attributes will. * * To create a subgraph with its own copy of the edge/node attributes use: * `jsnx.Graph(G.subgraph(nbunch))` * * If edge attributes are containers, a deep copy can be obtained using: * `G.subgraph(nbunch).copy()`. * * ### Example * * ``` * var G = new jsnx.Graph(); * G.addPath([0,1,2,3]); * var H = G.subgraph([0,1,2]); * H.edges(); * // [[0,1], [1,2]] * ``` * * @param {NodeContainer=} nbunch A container of nodes which will be * iterated through once. * @return {MultiGraph} A subgraph of the graph with the same edge attributes. */ value: function subgraph(nbunch) { var bunch = this.nbunchIter(nbunch); // create new graph and copy subgraph into it var H = new this.constructor(); // copy node and attribute dictionaries this.node.forEach(function (d, n) { return H.node.set(n, d); }); // namespace shortcuts for speed var HAdj = H.adj, thisAdj = this.adj; // add nodes and edges (undirected method) var _iteratorNormalCompletion7 = true; var _didIteratorError7 = false; var _iteratorError7 = undefined; try { for (var _iterator7 = _getIterator(bunch), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) { var n = _step7.value; var Hnbrs = new _internals.Map(); HAdj.set(n, Hnbrs); var _iteratorNormalCompletion8 = true; var _didIteratorError8 = false; var _iteratorError8 = undefined; try { for (var _iterator8 = _getIterator(thisAdj.get(n)), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) { var _step8$value = _slicedToArray(_step8.value, 2); var nbr = _step8$value[0]; var edgedict = _step8$value[1]; if (HAdj.has(nbr)) { // add both representations of edge: n-nbr and nbr-n // they share the same edgedict var ed = (0, _internals.clone)(edgedict); Hnbrs.set(nbr, ed); HAdj.get(nbr).set(n, ed); } } } catch (err) { _didIteratorError8 = true; _iteratorError8 = err; } finally { try { if (!_iteratorNormalCompletion8 && _iterator8['return']) { _iterator8['return'](); } } finally { if (_didIteratorError8) { throw _iteratorError8; } } } } } catch (err) { _didIteratorError7 = true; _iteratorError7 = err; } finally { try { if (!_iteratorNormalCompletion7 && _iterator7['return']) { _iterator7['return'](); } } finally { if (_didIteratorError7) { throw _iteratorError7; } } } H.graph = this.graph; return H; } }], [{ key: '__name__', /** * Holds the graph type (class) name for information. * This is compatible to Pythons __name__ property. * * @type {string} */ get: function get() { return 'MultiGraph'; } }]); return MultiGraph; })(_Graph3['default']); exports['default'] = MultiGraph; module.exports = exports['default']; /*eslint no-loop-func:0*/