jsnetworkx
Version:
A graph processing and visualization library for JavaScript (port of NetworkX for Python).
1,767 lines (1,586 loc) • 64.3 kB
JavaScript
'use strict';
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$assign = require('babel-runtime/core-js/object/assign')['default'];
var _Array$from = require('babel-runtime/core-js/array/from')['default'];
var _getIterator = require('babel-runtime/core-js/get-iterator')['default'];
var _regeneratorRuntime = require('babel-runtime/regenerator')['default'];
var _Symbol$iterator = require('babel-runtime/core-js/symbol/iterator')['default'];
var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default'];
var _interopRequireWildcard = require('babel-runtime/helpers/interop-require-wildcard')['default'];
Object.defineProperty(exports, '__esModule', {
value: true
});
var _exceptionsKeyError = require('../exceptions/KeyError');
var _exceptionsKeyError2 = _interopRequireDefault(_exceptionsKeyError);
var _internalsMap = require('../_internals/Map');
var _internalsMap2 = _interopRequireDefault(_internalsMap);
var _internalsSet = require('../_internals/Set');
var _internalsSet2 = _interopRequireDefault(_internalsSet);
var _exceptionsJSNetworkXError = require('../exceptions/JSNetworkXError');
var _exceptionsJSNetworkXError2 = _interopRequireDefault(_exceptionsJSNetworkXError);
var _lodashLangIsBoolean = require('lodash/lang/isBoolean');
var _lodashLangIsBoolean2 = _interopRequireDefault(_lodashLangIsBoolean);
var _lodashLangIsString = require('lodash/lang/isString');
var _lodashLangIsString2 = _interopRequireDefault(_lodashLangIsString);
var _convert = require('../convert');
var convert = _interopRequireWildcard(_convert);
var _internals = require('../_internals');
/*jshint expr:false*/
/*
* Base class for undirected graphs.
*
* A Graph stores nodes and edges with optional data, or attributes.
*
* Graphs hold undirected edges. Self loops are allowed but multiple
* (parallel) edges are not.
*
* Nodes can be strings, numbers or any object with a custom `toString` method.
*
* Edges are represented as links between nodes with optional
* key/value attributes.
*
* ### Examples
*
* Create an empty graph (a "null graph") with no nodes and no edges.
*
* ```
* var G = new jsnx.Graph();
* ```
*
* 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]);
* G.addNodesFrom(new Set('foo', 'bar'));
* var H = jsnx.completeGraph(10);
* G.addNodesFrom(H);
* ```
*
* In addition to strings, numbers and arrays, any object that implements a
* custom `toString` method can be used as node.
*
* #### Edges
*
* `G` can also be grown by adding edges.
*
* Add one edge,
*
* ```
* G.addEdge(1, 2);
* ```
*
* a list of edges,
*
* ```
* G.addEdgesFrom([[1,2], [1,3]]);
* ```
*
* or a collection of edges,
*
* ```
* G.addEdgesFrom(H.edges);
* ```
*
* If some edges connect nodes not yet in the graph, the nodes are added
* automatically. There are no errors when adding nodes or edges that already
* exist.
*
* #### Attributes
*
* Each graph, node and edge can hold key/value attribute pairs in an associated
* attribute object (keys must be strings or numbers).
* By default these are empty, but can added or changed using `addEdge`,
* `addNode`.
*
* ```
* var G = new jsnx.Graph(null, {day: 'Friday'});
* G.graph
* // {day: 'Friday'}
* ```
*
* Add node attributes using `addNode()` or `addNodesFrom()`:
*
* ```
* G.addNode(1, {time: '5pm'});
* G.addNodesFrom([2, [3, {time: '3pm'}]], {time: '2pm'});
* G.nodes(true);
* // [[1, {time: '5pm'}], [2, {time: '2pm'}], [3, {time: '3pm'}]]
* ```
*
* Add edge attributes using `addEdge()`, or `addEdgesFrom()`:
*
* ```
* G.addEdge(1, w, {weight: 4.7});
* G.addEdgesFrom([[3,4], [4,5]], {color: 'red'});
* ```
*
* @see DiGraph
* @see MultiGraph
* @see MultiDiGraph
*/
var Graph = (function () {
/*
* @param {Iterable} optData Data to initialize graph. If `data` is not
* provided, an empty graph is created. The data can be an edge list, or
* any JSNetworkX graph object.
* @param {Object=} optAttr (default=no attributes)
* Attributes to add to graph as key=value pairs.
*/
function Graph(optData, optAttr) {
_classCallCheck(this, Graph);
// makes it possible to call Graph without new
if (!(this instanceof Graph)) {
return new Graph(optData, optAttr);
}
this.graph = {}; // dictionary for graph attributes
this.node = new _internalsMap2['default'](); // empty node dict (created before convert)
this.adj = new _internalsMap2['default'](); // empty adjacency dict
// attempt to load graph with data
if (optData != null) {
convert.toNetworkxGraph(optData, this);
}
// load graph attributes (must be after convert)
if (optAttr) {
_Object$assign(this.graph, optAttr);
}
this.edge = this.adj;
}
_createClass(Graph, [{
key: 'toString',
/**
* Return the graph name
*
* @return {string} Graph name.
*/
value: function toString() {
return this.name;
}
}, {
key: 'get',
/**
* Return a Map of neighbors of node n.
*
* @param {Node} n A node in the graph.
*
* @return {!Map} The adjacency dictionary for nodes connected to n.
*/
value: function get(n) {
var value = this.adj.get(n);
if (typeof value === 'undefined') {
throw new _exceptionsKeyError2['default']('Graph does not contain node ' + n + '.');
}
return value;
}
}, {
key: 'addNode',
/**
* Add a single node n and update node attributes.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addNode(1);
* G.addNode('Hello');
* G.numberOfNodes();
* 2
* ```
*
* @see #addNodesFrom
*
* @param {Node} n Node
* @param {Object=} opt_attr_dict Dictionary of node attributes.
* Key/value pairs will update existing data associated with the node.
*/
value: function addNode(n) {
var optAttrDict = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
if (!(0, _internals.isPlainObject)(optAttrDict)) {
throw new _exceptionsJSNetworkXError2['default']('The attr_dict argument must be an object.');
}
if (!this.node.has(n)) {
this.adj.set(n, new _internalsMap2['default']());
this.node.set(n, optAttrDict);
} else {
// update attr even if node already exists
_Object$assign(this.node.get(n), optAttrDict);
}
}
}, {
key: 'addNodesFrom',
/**
* Add multiple nodes.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph(); // or DiGraph, MultiGraph, MultiDiGraph
* G.addNodesFrom([1,2,3]);
* G.nodes();
* // [1,2,3]
* ```
*
* Use the second argument to update specific node attributes for every node.
*
* ```
* G.addNodesFrom([1,2], {size: 10});
* G.addNodesFrom([2,3], {weight: 0.4});
* ```
*
* Use `(node, object)` tuples to update attributes for specific nodes.
*
* ```
* G.addNodesFrom([[1, {size: 11}], [2, {color: 'blue'}]]);
* G.node.get(1).size
* // 11
* var H = new jsnx.Graph();
* H.addNodesFrom(G.nodes(true));
* H.node.get(1).size
* // 11
* ```
*
* @see #addNode
*
* @param {Iterable} nodes
* An iterable of nodes
* OR
* An iterable of (node, object) tuples.
*
* @param {Object=} optAttr Update attributes for all nodes in nodes.
* Node attributes specified in nodes as a tuple
* take precedence over attributes specified generally.
*/
value: function addNodesFrom(nodes) {
var optAttr = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
(0, _internals.forEach)(nodes, function (node) {
if (Array.isArray(node) && node.length === 2 && (0, _internals.isPlainObject)(node[1])) {
var _node = _slicedToArray(node, 2);
var nn = _node[0];
var ndict = _node[1];
if (!this.adj.has(nn)) {
this.adj.set(nn, new _internalsMap2['default']());
var newdict = (0, _internals.clone)(optAttr);
this.node.set(nn, _Object$assign(newdict, ndict));
} else {
var olddict = this.node.get(nn);
_Object$assign(olddict, optAttr, ndict);
}
return; // continue next iteration
}
var newnode = !this.node.has(node);
if (newnode) {
this.adj.set(node, new _internalsMap2['default']());
this.node.set(node, (0, _internals.clone)(optAttr));
} else {
_Object$assign(this.node.get(node), optAttr);
}
}, this);
}
}, {
key: 'removeNode',
/**
* Remove node n.
*
* Removes the node n and all adjacent edges.
* Attempting to remove a non-existent node will raise an exception.
*
* ### Example
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2]);
* G.edges();
* // [[0,1], [1,2]]
* G.removeNode(1);
* G.edges();
* // []
* ```
*
* @see #removeNodesFrom
*
* @param {Node} n A node in the graph
*/
value: function removeNode(n) {
var adj = this.adj;
if (this.node['delete'](n)) {
adj.get(n).forEach(function (_, u) {
return adj.get(u)['delete'](n) // remove all edges n-u in graph
;
});
adj['delete'](n); // now remove node
} else {
throw new _exceptionsJSNetworkXError2['default']('The node %s is not in the graph', n);
}
}
}, {
key: 'removeNodesFrom',
/**
* Remove multiple nodes.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2]);
* var e = G.nodes(); // [0,1,2]
* G.removeNodesFrom(e);
* G.nodes();
* // []
* ```
*
* @see #removeNode
*
* @param {Iterable} nodes A container of nodes.
* If a node in the container is not in the graph it is silently ignored.
*/
value: function removeNodesFrom(nodes) {
var adj = this.adj;
var node = this.node;
(0, _internals.forEach)(nodes, function (n) {
if (node['delete'](n)) {
adj.get(n).forEach(function (_, u) {
return adj.get(u)['delete'](n);
});
adj['delete'](n);
}
});
}
}, {
key: 'nodesIter',
/**
* Return an iterator over the nodes.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2]);
* var data = [];
* Array.from(G.nodesIter(true)).map(([node, data]) => data);
* // [{}, {}, {}]
* ```
*
* ### Notes
*
* If the node is not required, it is simpler and equivalent to use `G`, e.g.
* `Array.from(G)` or `for (var node of G)`.
*
* @param {boolean=} optData If false the iterator returns
* nodes. If true return a two-tuple of node and node data dictionary.
*
* @return {Iterator} of nodes If data=true the iterator gives
* two-tuples containing (node, node data, dictionary).
*/
value: function nodesIter() {
var optData = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0];
if (optData) {
return (0, _internals.toIterator)(this.node);
}
return this.node.keys();
}
}, {
key: 'nodes',
/**
* Return a list of the nodes in the graph.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2]);
* G.nodes();
* // [0,1,2]
* G.addNode(1, {time: '5pm'});
* G.nodes(true);
* // [[0,{}], [1,{time: '5pm'}], [2,{}]]
* ```
*
* @param {boolean=} optData If false the iterator returns
* nodes. If true return a two-tuple of node and node data dictionary.
*
* @return {!Array} of nodes If data=true a list of two-tuples containing
* (node, node data object).
*/
value: function nodes() {
var optData = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0];
return _Array$from(optData ? this.node.entries() : this.node.keys());
}
}, {
key: 'numberOfNodes',
/**
* Return the number of nodes in the graph.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2]);
* G.numberOfNodes();
* // 3
* ```
*
* @see #order
*
* @return {number} The number of nodes in the graph.
*/
value: function numberOfNodes() {
return this.node.size;
}
}, {
key: 'order',
/**
* Return the number of nodes in the graph.
*
* @see #numberOfNodes
*
* @return {number} The number of nodes in the graph.
*/
value: function order() {
return this.node.size;
}
}, {
key: 'hasNode',
/**
* Return true if the graph contains the node n.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2]);
* G.hasNode(0);
* // true
* ```
*
* @param {Node} n node.
* @return {boolean}
*/
value: function hasNode(n) {
return this.node.has(n);
}
}, {
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 object with key/value pairs as third argument.
*
*
* ### Examples
*
* The following all add the edge `(1,2)` to graph `G`:
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addEdge(1,2);
* G.addEdgesFrom([[1,2]]);
* ```
*
* Associate data to edges using an object:
*
* ```
* G.addEdge(1, 2, {weight: 3});
* G.addEdge(1, 3, {weight: 7, capacity: 15, length: 342.7});
* ```
*
* ### Notes
*
* Adding an edge that already exists updates the edge data.
*
* Many algorithms designed for weighted graphs use as the edge weight a
* numerical value assigned to an attribute which by default is 'weight'.
*
* @see #addEdgesFrom
*
* @param {Node} u Node
* @param {Node} v Node
* @param {Object=} optAttrDict Object of edge attributes.
* Key/value pairs will update existing data associated with the edge.
*/
value: function addEdge(u, v, optAttrDict) {
if (optAttrDict && !(0, _internals.isPlainObject)(optAttrDict)) {
throw new _exceptionsJSNetworkXError2['default']('The attr_dict argument must be an object.');
}
// add nodes
if (!this.node.has(u)) {
this.adj.set(u, new _internalsMap2['default']());
this.node.set(u, {});
}
if (!this.node.has(v)) {
this.adj.set(v, new _internalsMap2['default']());
this.node.set(v, {});
}
// add the edge
var datadict = this.adj.get(u).get(v) || {};
_Object$assign(datadict, optAttrDict);
this.adj.get(u).set(v, datadict);
this.adj.get(v).set(u, datadict);
}
}, {
key: 'addEdgesFrom',
/**
* Add all the edges in `ebunch`.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addEdgesFrom([[0,1], [1,2]]); // using a list of edges
* ```
*
* Associate data to edges
*
* ```
* G.addEdgesFrom([[1,2], [2,3]], {weight: 3});
* G.addEdgesFrom([[3,4], [1,4]], {label: 'WN2898'});
* ```
*
* ### Notes
*
* Adding the same edge twice has no effect but any edge data
* will be updated when each duplicate edge is added.
*
* @see #add_edge
* @see #addWeightedEdgesFrom
*
* @param {Iterable} ebunch container of edges
* Each edge given in the container will be added to the
* graph. The edges must be given as as 2-tuples (u,v) or
* 3-tuples (u,v,d) where d is a dictionary containing edge data.
*
* @param {Object=} optAttrDict Object of edge attributes.
* Dictionary of edge attributes. Key/value pairs will
* update existing data associated with each edge.
*/
value: function addEdgesFrom(ebunch, optAttrDict) {
if (optAttrDict && !(0, _internals.isPlainObject)(optAttrDict)) {
throw new _exceptionsJSNetworkXError2['default']('The attr_dict argument must be an object.');
}
// process ebunch
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = _getIterator(ebunch), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var tuple = _step.value;
if (tuple.length == null) {
throw new _exceptionsJSNetworkXError2['default']((0, _internals.sprintf)('Edge tuple %j must be a 2-tuple or 3-tuple.', tuple));
}
var _tuple = _slicedToArray(tuple, 3);
var u = _tuple[0];
var v = _tuple[1];
var data = _tuple[2];
if (!(0, _internals.isPlainObject)(data)) {
data = {};
}
if (u == null || v == null || tuple[3] != null) {
throw new _exceptionsJSNetworkXError2['default']((0, _internals.sprintf)('Edge tuple %j must be a 2-tuple or 3-tuple.', tuple));
}
if (!this.node.has(u)) {
this.adj.set(u, new _internalsMap2['default']());
this.node.set(u, {});
}
if (!this.node.has(v)) {
this.adj.set(v, new _internalsMap2['default']());
this.node.set(v, {});
}
// add the edge
var datadict = this.adj.get(u).get(v) || {};
_Object$assign(datadict, optAttrDict, data);
this.adj.get(u).set(v, datadict);
this.adj.get(v).set(u, datadict);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator['return']) {
_iterator['return']();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
}, {
key: 'addWeightedEdgesFrom',
/**
* Add all the edges in `ebunch` as weighted edges with specified weights.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addWeightedEdgesFrom([[0,1,3.0], [1,2,7.5]]);
* ```
*
* ### Note
*
* Adding the same edge twice for Graph/DiGraph simply updates
* the edge data. For MultiGraph/MultiDiGraph, duplicate edges
* are stored.
*
* @see #addEdge
* @see #addEdgesFrom
*
* @param {Iterable} ebunch container of edges
* Each edge given in the list or container will be added
* to the graph. The edges must be given as 3-tuples (u,v,w)
* where w is a number.
* @param {string=} optWeight (default 'weight')
* The attribute name for the edge weights to be added.
* @param {Object=} optAttr Edge attributes to add/update for all edges.
*/
value: function addWeightedEdgesFrom(ebunch, optWeight, optAttr) {
optAttr = optAttr || {};
if (!(0, _lodashLangIsString2['default'])(optWeight)) {
optAttr = optWeight;
optWeight = 'weight';
}
this.addEdgesFrom((0, _internals.mapSequence)(ebunch, function (e) {
var attr = {};
attr[optWeight] = e[2];
if (attr[optWeight] == null) {
// simulate too few value to unpack error
throw new TypeError('Values must consist of three elements: %s.', e);
}
return [e[0], e[1], attr];
}), optAttr);
}
}, {
key: 'removeEdge',
/**
* Remove the edge between u and v.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2,3]);
* G.removeEdge(0,1);
* ```
*
* @see #removeEdgesFrom
*
* @param {Node} u Node
* @param {Node} v Node
*/
value: function removeEdge(u, v) {
var node = this.adj.get(u);
if (node != null) {
node['delete'](v);
// self-loop needs only one entry removed
var vnode = this.adj.get(v);
if (vnode !== node) {
vnode['delete'](u);
}
} else {
throw new _exceptionsJSNetworkXError2['default']('The edge %s-%s is not in the graph', u, v);
}
}
}, {
key: 'removeEdgesFrom',
/**
* Remove all edges specified in `ebunch`.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2,3]);
* var ebunch = [[1,2], [2,3]];
* G.removeEdgesFrom(ebunch);
* ```
*
* ### Notes
*
* Will fail silently if an edge in `ebunch` is not in the graph.
*
* @param {Iterable} ebunch Iterable 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) edge between u and v.
* - 3-tuples (u,v,k) where k is ignored.
*/
value: function removeEdgesFrom(ebunch) {
var adj = this.adj;
(0, _internals.forEach)(ebunch, function (_ref) {
var _ref2 = _slicedToArray(_ref, 2);
var u = _ref2[0];
var v = _ref2[1];
var unode = adj.get(u);
if (unode != null && unode.has(v)) {
unode['delete'](v);
var vnode = adj.get(v);
if (vnode !== unode) {
vnode['delete'](u);
}
}
});
}
}, {
key: 'hasEdge',
/**
* Return True if the edge (u,v) is in the graph.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2,3]);
* G.hasEdge(0, 1);
* // true
* var edge = [0, 1];
* G.hasEdge.apply(G, edge);
* // true
* ```
*
* @param {Node} u Node.
* @param {Node} v Node.
*
* @return {boolean} True if edge is in the graph, False otherwise.
*/
value: function hasEdge(u, v) {
var unode = this.adj.get(u);
return unode && unode.has(v);
}
}, {
key: 'neighbors',
/**
* Return a list of the nodes connected to the node n.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2,3]);
* G.neighbors(0);
* // [1]
* ```
*
* ### Notes
*
* It can be more convenient to access the adjacency map as `G.get(n)`:
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addEdge('a', 'b', {weight: 7});
* G.get('a');
* // Map {'b': {weight: 7}}
* ```
*
* @param {!Node} n A node in the graph.
* @return {!Array} A list of nodes that are adjacent to n.
*/
value: function neighbors(n) {
return _Array$from(this.neighborsIter(n));
}
}, {
key: 'neighborsIter',
/**
* Return an iterator over all neighbors of node n.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2,3]);
* Array.from(G.neighborsIter(0));
* // [1]
* ```
*
* You could also iterate over the adjacency map instead:
*
* ```
* Array.from(G.get(0).keys());
* ```
*
* @param {!Node} n A node in the graph.
* @return {!Iterator} A list of nodes that are adjacent to n.
*/
value: function neighborsIter(n) {
var node = this.adj.get(n);
if (node != null) {
return node.keys();
} else {
throw new _exceptionsJSNetworkXError2['default']('The node %s is not in the graph.', n);
}
}
}, {
key: 'edges',
/**
* Return a list of edges.
*
* Edges are returned as tuples with optional data
* in the order (node, neighbor, data).
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2]);
* G.addEdge(2, 3, {weight: 5});
* G.edges();
* // [[0,1], [1,2], [2,3]]
* G.edges(true);
* // [[0,1,{}], [1,2,{}], [2,3, {weight: 5}]
* G.edges([0,3]);
* // [[0,1], [3,2]]
* G.edges(0);
* // [[0,1]]
* ```
*
* ### Note
*
* Nodes in `nbunch` that are not in the graph will be (quietly) ignored.
* For directed graphs this returns the out-edges.
*
* @param {?(Node|Iterable)=} optNbunch A container of nodes.
* The container will be iterated through once.
* @param {?boolean=} optData Return two tuples (u,v) (False)
* or three-tuples (u,v,data) (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) {
var optData = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
return _Array$from(this.edgesIter(optNbunch, optData));
}
}, {
key: 'edgesIter',
/**
* Return an iterator over the edges.
*
* Edges are returned as tuples with optional data
* in the order (node, neighbor, data).
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2]);
* G.addEdge(2, 3, {weight: 5});
* Array.from(G.edgesIter());
* // [[0,1], [1,2], [2,3]]
* Array.from(G.edgesIter(true));
* // [[0,1,{}], [1,2,{}], [2,3, {weight: 5}]
* Array.from(G.edgesIter([0,3]));
* // [[0,1], [3,2]]
* Array.from(G.edgesIter(0));
* // [[0,1]]
* ```
*
* ### Note
*
* Nodes in `nbunch` that are not in the graph will be (quietly) ignored.
* For directed graphs this returns the out-edges.
*
* @param {?(Node|Iterable)=} optNbunch A container of nodes.
* The container will be iterated through once.
* @param {?boolean=} optData Return two tuples (u,v) (False)
* or three-tuples (u,v,data) (True).
* @return {!Iterator} iterater if `(u,v)` or `(u,v,d)` edge tuples
*/
value: _regeneratorRuntime.mark(function edgesIter(optNbunch) {
var optData = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];
var seen, nodesNbrs, adj, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, nodeData, node, _iteratorNormalCompletion3, _didIteratorError3, _iteratorError3, _iterator3, _step3, neighborsData;
return _regeneratorRuntime.wrap(function edgesIter$(context$2$0) {
while (1) switch (context$2$0.prev = context$2$0.next) {
case 0:
// handle calls with data being the only argument
if ((0, _lodashLangIsBoolean2['default'])(optNbunch)) {
optData = optNbunch;
optNbunch = null;
}
seen = new _internalsSet2['default']();
if (optNbunch == null) {
nodesNbrs = this.adj.entries();
} else {
adj = this.adj;
nodesNbrs = (0, _internals.mapIterator)(this.nbunchIter(optNbunch), function (n) {
return (0, _internals.tuple2)(n, adj.get(n));
});
}
_iteratorNormalCompletion2 = true;
_didIteratorError2 = false;
_iteratorError2 = undefined;
context$2$0.prev = 6;
_iterator2 = _getIterator(nodesNbrs);
case 8:
if (_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done) {
context$2$0.next = 49;
break;
}
nodeData = _step2.value;
node = nodeData[0];
_iteratorNormalCompletion3 = true;
_didIteratorError3 = false;
_iteratorError3 = undefined;
context$2$0.prev = 14;
_iterator3 = _getIterator(nodeData[1].entries());
case 16:
if (_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done) {
context$2$0.next = 30;
break;
}
neighborsData = _step3.value;
if (seen.has(neighborsData[0])) {
context$2$0.next = 27;
break;
}
if (!optData) {
context$2$0.next = 25;
break;
}
neighborsData.unshift(node);
context$2$0.next = 23;
return neighborsData;
case 23:
context$2$0.next = 27;
break;
case 25:
context$2$0.next = 27;
return [node, neighborsData[0]];
case 27:
_iteratorNormalCompletion3 = true;
context$2$0.next = 16;
break;
case 30:
context$2$0.next = 36;
break;
case 32:
context$2$0.prev = 32;
context$2$0.t0 = context$2$0['catch'](14);
_didIteratorError3 = true;
_iteratorError3 = context$2$0.t0;
case 36:
context$2$0.prev = 36;
context$2$0.prev = 37;
if (!_iteratorNormalCompletion3 && _iterator3['return']) {
_iterator3['return']();
}
case 39:
context$2$0.prev = 39;
if (!_didIteratorError3) {
context$2$0.next = 42;
break;
}
throw _iteratorError3;
case 42:
return context$2$0.finish(39);
case 43:
return context$2$0.finish(36);
case 44:
seen.add(node);
nodeData.length = 0;
case 46:
_iteratorNormalCompletion2 = true;
context$2$0.next = 8;
break;
case 49:
context$2$0.next = 55;
break;
case 51:
context$2$0.prev = 51;
context$2$0.t1 = context$2$0['catch'](6);
_didIteratorError2 = true;
_iteratorError2 = context$2$0.t1;
case 55:
context$2$0.prev = 55;
context$2$0.prev = 56;
if (!_iteratorNormalCompletion2 && _iterator2['return']) {
_iterator2['return']();
}
case 58:
context$2$0.prev = 58;
if (!_didIteratorError2) {
context$2$0.next = 61;
break;
}
throw _iteratorError2;
case 61:
return context$2$0.finish(58);
case 62:
return context$2$0.finish(55);
case 63:
case 'end':
return context$2$0.stop();
}
}, edgesIter, this, [[6, 51, 55, 63], [14, 32, 36, 44], [37,, 39, 43], [56,, 58, 62]]);
})
}, {
key: 'getEdgeData',
/**
* Return the attribute object associated with edge (u,v).
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2,3]);
* G.getEdgeData(0,1);
* // {}
* ```
*
* If the edge exists, it may be simpler to access `G.get(0).get(1)`.
*
* @param {Node} u Node.
* @param {Node} v Node.
* @param {*} optDefault
* Value to return if the edge (u,v) is not found.
* @return {*} The edge attribute object.
*/
value: function getEdgeData(u, v) {
var optDefault = arguments.length <= 2 || arguments[2] === undefined ? null : arguments[2];
var nbrs = this.adj.get(u);
if (nbrs != null) {
var data = nbrs.get(v);
if (data != null) {
return data;
}
}
return optDefault;
}
}, {
key: 'adjacencyList',
/**
* Return an adjacency list representation of the graph.
*
* The output adjacency list is in the order of G.nodes().
* For directed graphs, only outgoing adjacencies are included.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2,3]);
* G.adjacencyList();
* // [[1], [0,2], [1,3], [2]]
* ```
*
* @return {!Array.<Array>} The adjacency structure of the graph as a
* list of lists.
*/
value: function adjacencyList() {
/*eslint no-unused-vars:0*/
return _Array$from((0, _internals.mapIterator)(this.adjacencyIter(), function (_ref3) {
var _ref32 = _slicedToArray(_ref3, 2);
var _ = _ref32[0];
var adj = _ref32[1];
return _Array$from(adj.keys());
}));
}
}, {
key: 'adjacencyIter',
/**
* Return an iterator of (node, adjacency map) tuples for all nodes.
*
* For directed graphs, only outgoing adjacencies are included.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2,3]);
* Array.from(G.adjacencyIter());
* // [
* // [0, Map {1: {}}],
* // [1, Map {0: {}, 2: {}}],
* // [2, Map {1: {}, 3: {}}],
* // [3, Map {2: {}]]
* // ]
* ```
*
* @return {!Iterator} An array of (node, adjacency map) tuples
* for all nodes in the graph.
*/
value: function adjacencyIter() {
return this.adj.entries();
}
}, {
key: 'degree',
/**
* Return the degree of a node or nodes.
*
* The node degree is the number of edges adjacent to that node.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph(); // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2,3])
* G.degree(0)
* // 1
* G.degree([0,1])
* // Map {0: 1, 1: 2}
* Array.from(G.degree([0,1]).values())
* // [1, 2]
* ```
*
* @param {(Node|Iterable)=} optNbunch (default=all nodes)
* An iterable of nodes. The iterable will be iterated
* through once.
* @param {string=} optWeight
* The edge attribute that holds the numerical value used
* as a weight. If null or not defined, then each edge has weight 1.
* The degree is the sum of the edge weights adjacent to the node.
* @return {!(number|Map)} A dictionary with nodes as keys and
* degree as values or a number if a single node is specified.
*/
value: function degree(optNbunch, optWeight) {
if (optNbunch != null && this.hasNode(optNbunch)) {
// return a single node
return this.degreeIter(optNbunch, optWeight).next().value[1];
} else {
return new _internalsMap2['default'](this.degreeIter(optNbunch, optWeight));
}
}
}, {
key: 'degreeIter',
/**
* Return an array for (node, degree).
*
* The node degree is the number of edges adjacent to that node.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph(); // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2,3])
* Array.from(G.degreeIter(0));
* // [[0, 1]]
* Array.from(G.degreeIter([0,1]));
* // [[0, 1], [1, 2]]
* ```
*
* @param {(Node|Iterable)=} optNbunch (default=all nodes)
* 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 null or not defined, then each edge has weight 1.
* The degree is the sum of the edge weights adjacent to the node.
* @return {!Iterator} of two-tuples of (node, degree).
*
* @export
*/
value: function degreeIter(optNbunch, optWeight) {
// istanbul ignore next
var _this = this;
var nodesNbrs;
var iterator;
if (optNbunch == null) {
nodesNbrs = this.adj.entries();
} else {
(function () {
var adj = _this.adj;
nodesNbrs = (0, _internals.mapIterator)(_this.nbunchIter(optNbunch), function (n) {
return (0, _internals.tuple2)(n, adj.get(n));
});
})();
}
if (!optWeight) {
iterator = (0, _internals.mapIterator)(nodesNbrs, function (_ref4) {
var _ref42 = _slicedToArray(_ref4, 2);
var node = _ref42[0];
var nbrs = _ref42[1];
return [node, nbrs.size + +nbrs.has(node)];
});
} else {
iterator = (0, _internals.mapIterator)(nodesNbrs, function (_ref5) {
var _ref52 = _slicedToArray(_ref5, 2);
var n = _ref52[0];
var nbrs = _ref52[1];
var sum = 0;
nbrs.forEach(function (data) {
var weight = data[optWeight];
sum += +(weight != null ? weight : 1);
});
if (nbrs.has(n)) {
var weight = nbrs.get(n)[optWeight];
sum += +(weight != null ? weight : 1);
}
return [n, sum];
});
}
return iterator;
}
}, {
key: 'clear',
/**
* Remove all nodes and edges from the graph.
*
* This also removes the name, and all graph, node, and edge attributes.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph(); // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2,3]);
* G.clear();
* G.nodes();
* // []
* G.edges();
* // []
* ```
*/
value: function clear() {
this.name = '';
this.adj.clear();
this.node.clear();
(0, _internals.clear)(this.graph);
}
}, {
key: 'copy',
/**
* Return a copy of the graph.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph(); // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2,3]);
* var H = G.copy();
* ```
*
* ### Notes
*
* This makes a complete copy of the graph including all of the
* node or edge attributes.
*
* @return {!Graph}
*/
value: function copy() {
return (0, _internals.deepcopy)(this);
}
}, {
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 false;
}
}, {
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.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph(); // or MultiGraph, etc
* G.addPath([0,1]);
* var H = G.toDirected();
* H.edges();
* // [[0,1], [1,0]]
* ```
*
* If already directed, return a (deep) copy
*
* ```
* var G = new jsnx.DiGraph(); // or MultiDiGraph, etc
* G.addPath([0,1]);
* var H = G.toDirected();
* H.edges();
* // [[0,1]]
* ```
*
* ### 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 `var H = new jsnx.DiGraph(G)` which
* returns a shallow copy of the data.
*
* @return {!DiGraph}
* 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('./DiGraph'))();
G.name = this.name;
G.addNodesFrom(this);
G.addEdgesFrom(_regeneratorRuntime.mark(function callee$2$0() {
var _iteratorNormalCompletion4, _didIteratorError4, _iteratorError4, _iterator4, _step4, nd, u, _iteratorNormalCompletion5, _didIteratorError5, _iteratorError5, _iterator5, _step5, nbr;
return _regeneratorRuntime.wrap(function callee$2$0$(context$3$0) {
while (1) switch (context$3$0.prev = context$3$0.next) {
case 0:
_iteratorNormalCompletion4 = true;
_didIteratorError4 = false;
_iteratorError4 = undefined;
context$3$0.prev = 3;
_iterator4 = _getIterator(this.adjacencyIter());
case 5:
if (_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done) {
context$3$0.next = 37;
break;
}
nd = _step4.value;
u = nd[0];
_iteratorNormalCompletion5 = true;
_didIteratorError5 = false;
_iteratorError5 = undefined;
context$3$0.prev = 11;
_iterator5 = _getIterator(nd[1]);
case 13:
if (_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done) {
context$3$0.next = 20;
break;
}
nbr = _step5.value;
context$3$0.next = 17;
return (0, _internals.tuple3)(u, nbr[0], (0, _internals.deepcopy)(nbr[1]));
case 17:
_iteratorNormalCompletion5 = true;
context$3$0.next = 13;
break;
case 20:
context$3$0.next = 26;
break;
case 22:
context$3$0.prev = 22;
context$3$0.t0 = context$3$0['catch'](11);
_didIteratorError5 = true;
_iteratorError5 = context$3$0.t0;
case 26:
context$3$0.prev = 26;
context$3$0.prev = 27;
if (!_iteratorNormalCompletion5 && _iterator5['return']) {
_iterator5['return']();
}
case 29:
context$3$0.prev = 29;
if (!_didIteratorError5) {
context$3$0.next = 32;
break;
}
throw _iteratorError5;
case 32:
return context$3$0.finish(29);
case 33:
return context$3$0.finish(26);
case 34:
_iteratorNormalCompletion4 = true;
context$3$0.next = 5;
break;
case 37:
context$3$0.next = 43;
break;
case 39:
context$3$0.prev = 39;
context$3$0.t1 = context$3$0['catch'](3);
_didIteratorError4 = true;
_iteratorError4 = context$3$0.t1;
case 43:
context$3$0.prev = 43;
context$3$0.prev = 44;
if (!_iteratorNormalCompletion4 && _iterator4['return']) {
_iterator4['return']();
}
case 46:
context$3$0.prev = 46;
if (!_didIteratorError4) {
context$3$0.next = 49;
break;
}
throw _iteratorError4;
case 49:
return context$3$0.finish(46);
case 50:
return context$3$0.finish(43);
case 51:
case 'end':
return context$3$0.stop();
}
}, callee$2$0, this, [[3, 39, 43, 51], [11, 22, 26, 34], [27,, 29, 33], [44,, 46, 50]]);
}).call(this));
G.graph = (0, _internals.deepcopy)(this.graph);
G.node = (0, _internals.deepcopy)(this.node);
return G;
}
}, {
key: 'toUndirected',
/**
* Return an undirected copy of the graph.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph(); // or MultiGraph, etc
* G.addPath([0,1]);
* var H = G.toDirected();
* G.edges();
* // [[0,1], [1,0]]
* var G2 = H.toUndirected();
* G2.edges();
* // [[0,1]]
* ```
*
* ### 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 `var H = new jsnx.Graph(G);` which
* returns a shallow copy of the data.
*
* @return {!Graph} A deepcopy of the graph.
* @export
*/
value: function toUndirected() {
return (0, _internals.deepcopy)(this);
}
}, {
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.
*
* ### Examples
*
* ```
* var G = new jsnx.Graph() // or DiGraph, MultiGraph, MultiDiGraph, etc
* G.addPath([0,1,2,3]);
* var H = G.subgraph([0,1,2]);
* H.edges();
* // [[0,1], [1,2]]
* ```
*
* ### 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:
* `new jsnx.Graph(G.subgraph(nbunch))`.
*
* For an inplace reduction of a graph to a subgraph you can remove nodes:
*
* ```
* G.removeNodesFrom(G.nodes().filter(function(n) {
* return nbunch.indexOf(n) > -1;
* }))
* ```
*
* @param {Iterable} nbunch
* An iterable of nodes which will be iterated through once.
* @return {Graph}
*/
value: function subgraph(nbunch) {
var bunch = this.nbunchIter(nbunch);
var n;
// create new graph and copy subgraph into it
var H = new this.constructor();
// copy node and attribute dictionaries
var _iteratorNormalCompletion6 = true;
var _didIteratorError6 = false;
var _iteratorError6 = undefined;
try {
for (var _iterator6 = _getIterator(bunch), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
n = _step6.value;
H.node.set(n, this.node.get(n));
}
} catch (err) {
_didIteratorError6 = true;
_iteratorError6 = err;
} finally {
try {
if (!_iteratorNormalCompletion6 && _iterator6['return']) {
_iterator6['return']();
}
} finally {
if (_didIteratorError6) {
throw _iteratorError6;
}
}
}
// namespace shortcuts for speed
var HAdj = H.adj;
var thisAdj = this.adj;
// add nodes and edges (undirected method)
var _iteratorNormalCompletion7 = true;
var _didIteratorError7 = false;
var _iteratorError7 = undefined;
try {
for (var _iterator7 = _getIterator(H), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
n = _step7.value;
var Hnbrs = new _internalsMap2['default']();
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 nbrdata = _step8.value;
var nbr = nbrdata[0];
var data = nbrdata[1];
if (HAdj.has(nbr)) {
// add both representations of edge: n-nbr and nbr-n
Hnbrs.set(nbr, data);
HAdj.get(nbr).set(n, data);
}
}
} 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 (_didIt