Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ngraph.graph

Package Overview
Dependencies
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ngraph.graph - npm Package Compare versions

Comparing version 0.0.4 to 0.0.5

685

index.js

@@ -5,3 +5,2 @@ /**

/**

@@ -14,368 +13,404 @@ * @example

*/
module.exports = function () {
// Graph structure is maintained as dictionary of nodes
// and array of links. Each node has 'links' property which
// hold all links related to that node. And general links
// array is used to speed up all links enumeration. This is inefficient
// in terms of memory, but simplifies coding.
module.exports = function() {
// Graph structure is maintained as dictionary of nodes
// and array of links. Each node has 'links' property which
// hold all links related to that node. And general links
// array is used to speed up all links enumeration. This is inefficient
// in terms of memory, but simplifies coding.
var nodes = typeof Object.create === 'function' ? Object.create(null) : {},
links = [],
// Hash of multi-edges. Used to track ids of edges between same nodes
multiEdges = {},
nodesCount = 0,
suspendEvents = 0,
var nodes = typeof Object.create === 'function' ? Object.create(null) : {},
links = [],
// Hash of multi-edges. Used to track ids of edges between same nodes
multiEdges = {},
nodesCount = 0,
suspendEvents = 0,
// Accumlates all changes made during graph updates.
// Each change element contains:
// changeType - one of the strings: 'add', 'remove' or 'update';
// node - if change is related to node this property is set to changed graph's node;
// link - if change is related to link this property is set to changed graph's link;
changes = [],
// Accumlates all changes made during graph updates.
// Each change element contains:
// changeType - one of the strings: 'add', 'remove' or 'update';
// node - if change is related to node this property is set to changed graph's node;
// link - if change is related to link this property is set to changed graph's link;
changes = [],
fireGraphChanged = function (graph) {
graph.fire('changed', changes);
},
fireGraphChanged = function(graph) {
graph.fire('changed', changes);
},
// Enter, Exit Mofidication allows bulk graph updates without firing events.
enterModification = function () {
suspendEvents += 1;
},
// Enter, Exit Mofidication allows bulk graph updates without firing events.
enterModification = function() {
suspendEvents += 1;
},
exitModification = function (graph) {
suspendEvents -= 1;
if (suspendEvents === 0 && changes.length > 0) {
fireGraphChanged(graph);
changes.length = 0;
}
},
exitModification = function(graph) {
suspendEvents -= 1;
if (suspendEvents === 0 && changes.length > 0) {
fireGraphChanged(graph);
changes.length = 0;
}
},
recordNodeChange = function (node, changeType) {
changes.push({node : node, changeType : changeType});
},
recordNodeChange = function(node, changeType) {
changes.push({
node: node,
changeType: changeType
});
},
recordLinkChange = function (link, changeType) {
changes.push({link : link, changeType : changeType});
},
linkConnectionSymbol = '👉 ';
recordLinkChange = function(link, changeType) {
changes.push({
link: link,
changeType: changeType
});
},
linkConnectionSymbol = '👉 ';
var graphPart = {
var graphPart = {
/**
* Adds node to the graph. If node with given id already exists in the graph
* its data is extended with whatever comes in 'data' argument.
*
* @param nodeId the node's identifier. A string or number is preferred.
* note: Node id should not contain 'linkConnectionSymbol'. This will break link identifiers
* @param [data] additional data for the node being added. If node already
* exists its data object is augmented with the new one.
*
* @return {node} The newly added node or node with given id if it already exists.
*/
addNode : function (nodeId, data) {
if (typeof nodeId === 'undefined') {
throw new Error('Invalid node identifier');
}
/**
* Adds node to the graph. If node with given id already exists in the graph
* its data is extended with whatever comes in 'data' argument.
*
* @param nodeId the node's identifier. A string or number is preferred.
* note: Node id should not contain 'linkConnectionSymbol'. This will break link identifiers
* @param [data] additional data for the node being added. If node already
* exists its data object is augmented with the new one.
*
* @return {node} The newly added node or node with given id if it already exists.
*/
addNode: function(nodeId, data) {
if (typeof nodeId === 'undefined') {
throw new Error('Invalid node identifier');
}
enterModification();
enterModification();
var node = this.getNode(nodeId);
if (!node) {
// TODO: Should I check for linkConnectionSymbol here?
node = new Node(nodeId);
nodesCount++;
var node = this.getNode(nodeId);
if (!node) {
// TODO: Should I check for linkConnectionSymbol here?
node = new Node(nodeId);
nodesCount++;
recordNodeChange(node, 'add');
} else {
recordNodeChange(node, 'update');
}
recordNodeChange(node, 'add');
} else {
recordNodeChange(node, 'update');
}
node.data = data;
node.data = data;
nodes[nodeId] = node;
nodes[nodeId] = node;
exitModification(this);
return node;
},
exitModification(this);
return node;
},
/**
* Adds a link to the graph. The function always create a new
* link between two nodes. If one of the nodes does not exists
* a new node is created.
*
* @param fromId link start node id;
* @param toId link end node id;
* @param [data] additional data to be set on the new link;
*
* @return {link} The newly created link
*/
addLink : function (fromId, toId, data) {
enterModification();
/**
* Adds a link to the graph. The function always create a new
* link between two nodes. If one of the nodes does not exists
* a new node is created.
*
* @param fromId link start node id;
* @param toId link end node id;
* @param [data] additional data to be set on the new link;
*
* @return {link} The newly created link
*/
addLink: function(fromId, toId, data) {
enterModification();
var fromNode = this.getNode(fromId) || this.addNode(fromId);
var toNode = this.getNode(toId) || this.addNode(toId);
var fromNode = this.getNode(fromId) || this.addNode(fromId);
var toNode = this.getNode(toId) || this.addNode(toId);
var linkId = fromId.toString() + linkConnectionSymbol + toId.toString();
var isMultiEdge = multiEdges.hasOwnProperty(linkId);
if (isMultiEdge || this.hasLink(fromId, toId)) {
if (!isMultiEdge) {
multiEdges[linkId] = 0;
}
linkId += '@' + (++multiEdges[linkId]);
}
var linkId = fromId.toString() + linkConnectionSymbol + toId.toString();
var isMultiEdge = multiEdges.hasOwnProperty(linkId);
if (isMultiEdge || this.hasLink(fromId, toId)) {
if (!isMultiEdge) {
multiEdges[linkId] = 0;
}
linkId += '@' + (++multiEdges[linkId]);
}
var link = new Link(fromId, toId, data, linkId);
var link = new Link(fromId, toId, data, linkId);
links.push(link);
links.push(link);
// TODO: this is not cool. On large graphs potentially would consume more memory.
fromNode.links.push(link);
toNode.links.push(link);
// TODO: this is not cool. On large graphs potentially would consume more memory.
fromNode.links.push(link);
toNode.links.push(link);
recordLinkChange(link, 'add');
recordLinkChange(link, 'add');
exitModification(this);
exitModification(this);
return link;
},
return link;
},
/**
* Removes link from the graph. If link does not exist does nothing.
*
* @param link - object returned by addLink() or getLinks() methods.
*
* @returns true if link was removed; false otherwise.
*/
removeLink : function (link) {
if (!link) { return false; }
var idx = indexOfElementInArray(link, links);
if (idx < 0) { return false; }
/**
* Removes link from the graph. If link does not exist does nothing.
*
* @param link - object returned by addLink() or getLinks() methods.
*
* @returns true if link was removed; false otherwise.
*/
removeLink: function(link) {
if (!link) {
return false;
}
var idx = indexOfElementInArray(link, links);
if (idx < 0) {
return false;
}
enterModification();
enterModification();
links.splice(idx, 1);
links.splice(idx, 1);
var fromNode = this.getNode(link.fromId);
var toNode = this.getNode(link.toId);
var fromNode = this.getNode(link.fromId);
var toNode = this.getNode(link.toId);
if (fromNode) {
idx = indexOfElementInArray(link, fromNode.links);
if (idx >= 0) {
fromNode.links.splice(idx, 1);
}
}
if (fromNode) {
idx = indexOfElementInArray(link, fromNode.links);
if (idx >= 0) {
fromNode.links.splice(idx, 1);
}
}
if (toNode) {
idx = indexOfElementInArray(link, toNode.links);
if (idx >= 0) {
toNode.links.splice(idx, 1);
}
}
if (toNode) {
idx = indexOfElementInArray(link, toNode.links);
if (idx >= 0) {
toNode.links.splice(idx, 1);
}
}
recordLinkChange(link, 'remove');
recordLinkChange(link, 'remove');
exitModification(this);
exitModification(this);
return true;
},
return true;
},
/**
* Removes node with given id from the graph. If node does not exist in the graph
* does nothing.
*
* @param nodeId node's identifier passed to addNode() function.
*
* @returns true if node was removed; false otherwise.
*/
removeNode: function (nodeId) {
var node = this.getNode(nodeId);
if (!node) { return false; }
/**
* Removes node with given id from the graph. If node does not exist in the graph
* does nothing.
*
* @param nodeId node's identifier passed to addNode() function.
*
* @returns true if node was removed; false otherwise.
*/
removeNode: function(nodeId) {
var node = this.getNode(nodeId);
if (!node) {
return false;
}
enterModification();
enterModification();
while (node.links.length) {
var link = node.links[0];
this.removeLink(link);
}
while (node.links.length) {
var link = node.links[0];
this.removeLink(link);
}
delete nodes[nodeId];
nodesCount--;
delete nodes[nodeId];
nodesCount--;
recordNodeChange(node, 'remove');
recordNodeChange(node, 'remove');
exitModification(this);
exitModification(this);
return true;
},
return true;
},
/**
* Gets node with given identifier. If node does not exist undefined value is returned.
*
* @param nodeId requested node identifier;
*
* @return {node} in with requested identifier or undefined if no such node exists.
*/
getNode : function (nodeId) {
return nodes[nodeId];
},
/**
* Gets node with given identifier. If node does not exist undefined value is returned.
*
* @param nodeId requested node identifier;
*
* @return {node} in with requested identifier or undefined if no such node exists.
*/
getNode: function(nodeId) {
return nodes[nodeId];
},
/**
* Gets number of nodes in this graph.
*
* @return number of nodes in the graph.
*/
getNodesCount : function () {
return nodesCount;
},
/**
* Gets number of nodes in this graph.
*
* @return number of nodes in the graph.
*/
getNodesCount: function() {
return nodesCount;
},
/**
* Gets total number of links in the graph.
*/
getLinksCount : function () {
return links.length;
},
/**
* Gets total number of links in the graph.
*/
getLinksCount: function() {
return links.length;
},
/**
* Gets all links (inbound and outbound) from the node with given id.
* If node with given id is not found null is returned.
*
* @param nodeId requested node identifier.
*
* @return Array of links from and to requested node if such node exists;
* otherwise null is returned.
*/
getLinks : function (nodeId) {
var node = this.getNode(nodeId);
return node ? node.links : null;
},
/**
* Gets all links (inbound and outbound) from the node with given id.
* If node with given id is not found null is returned.
*
* @param nodeId requested node identifier.
*
* @return Array of links from and to requested node if such node exists;
* otherwise null is returned.
*/
getLinks: function(nodeId) {
var node = this.getNode(nodeId);
return node ? node.links : null;
},
/**
* Invokes callback on each node of the graph.
*
* @param {Function(node)} callback Function to be invoked. The function
* is passed one argument: visited node.
*/
forEachNode : function (callback) {
if (typeof callback !== 'function') {
return;
}
var node;
/**
* Invokes callback on each node of the graph.
*
* @param {Function(node)} callback Function to be invoked. The function
* is passed one argument: visited node.
*/
forEachNode: createNodeIterator(),
for (node in nodes) {
if (callback(nodes[node])) {
return; // client doesn't want to proceed. return.
}
/**
* Invokes callback on every linked (adjacent) node to the given one.
*
* @param nodeId Identifier of the requested node.
* @param {Function(node, link)} callback Function to be called on all linked nodes.
* The function is passed two parameters: adjacent node and link object itself.
* @param oriented if true graph treated as oriented.
*/
forEachLinkedNode: function(nodeId, callback, oriented) {
var node = this.getNode(nodeId),
i,
link,
linkedNodeId;
if (node && node.links && typeof callback === 'function') {
// Extraced orientation check out of the loop to increase performance
if (oriented) {
for (i = 0; i < node.links.length; ++i) {
link = node.links[i];
if (link.fromId === nodeId) {
callback(nodes[link.toId], link);
}
},
}
} else {
for (i = 0; i < node.links.length; ++i) {
link = node.links[i];
linkedNodeId = link.fromId === nodeId ? link.toId : link.fromId;
/**
* Invokes callback on every linked (adjacent) node to the given one.
*
* @param nodeId Identifier of the requested node.
* @param {Function(node, link)} callback Function to be called on all linked nodes.
* The function is passed two parameters: adjacent node and link object itself.
* @param oriented if true graph treated as oriented.
*/
forEachLinkedNode : function (nodeId, callback, oriented) {
var node = this.getNode(nodeId),
i,
link,
linkedNodeId;
callback(nodes[linkedNodeId], link);
}
}
}
},
if (node && node.links && typeof callback === 'function') {
// Extraced orientation check out of the loop to increase performance
if (oriented) {
for (i = 0; i < node.links.length; ++i) {
link = node.links[i];
if (link.fromId === nodeId) {
callback(nodes[link.toId], link);
}
}
} else {
for (i = 0; i < node.links.length; ++i) {
link = node.links[i];
linkedNodeId = link.fromId === nodeId ? link.toId : link.fromId;
/**
* Enumerates all links in the graph
*
* @param {Function(link)} callback Function to be called on all links in the graph.
* The function is passed one parameter: graph's link object.
*
* Link object contains at least the following fields:
* fromId - node id where link starts;
* toId - node id where link ends,
* data - additional data passed to graph.addLink() method.
*/
forEachLink: function(callback) {
var i, length;
if (typeof callback === 'function') {
for (i = 0, length = links.length; i < length; ++i) {
callback(links[i]);
}
}
},
callback(nodes[linkedNodeId], link);
}
}
}
},
/**
* Suspend all notifications about graph changes until
* endUpdate is called.
*/
beginUpdate: function() {
enterModification();
},
/**
* Enumerates all links in the graph
*
* @param {Function(link)} callback Function to be called on all links in the graph.
* The function is passed one parameter: graph's link object.
*
* Link object contains at least the following fields:
* fromId - node id where link starts;
* toId - node id where link ends,
* data - additional data passed to graph.addLink() method.
*/
forEachLink : function (callback) {
var i, length;
if (typeof callback === 'function') {
for (i = 0, length = links.length; i < length; ++i) {
callback(links[i]);
}
}
},
/**
* Resumes all notifications about graph changes and fires
* graph 'changed' event in case there are any pending changes.
*/
endUpdate: function() {
exitModification(this);
},
/**
* Suspend all notifications about graph changes until
* endUpdate is called.
*/
beginUpdate : function () {
enterModification();
},
/**
* Removes all nodes and links from the graph.
*/
clear: function() {
var that = this;
that.beginUpdate();
that.forEachNode(function(node) {
that.removeNode(node.id);
});
that.endUpdate();
},
/**
* Resumes all notifications about graph changes and fires
* graph 'changed' event in case there are any pending changes.
*/
endUpdate : function () {
exitModification(this);
},
/**
* Detects whether there is a link between two nodes.
* Operation complexity is O(n) where n - number of links of a node.
*
* @returns link if there is one. null otherwise.
*/
hasLink: function(fromNodeId, toNodeId) {
// TODO: Use adjacency matrix to speed up this operation.
var node = this.getNode(fromNodeId),
i;
if (!node) {
return null;
}
/**
* Removes all nodes and links from the graph.
*/
clear : function () {
var that = this;
that.beginUpdate();
that.forEachNode(function (node) { that.removeNode(node.id); });
that.endUpdate();
},
for (i = 0; i < node.links.length; ++i) {
var link = node.links[i];
if (link.fromId === fromNodeId && link.toId === toNodeId) {
return link;
}
}
/**
* Detects whether there is a link between two nodes.
* Operation complexity is O(n) where n - number of links of a node.
*
* @returns link if there is one. null otherwise.
*/
hasLink : function (fromNodeId, toNodeId) {
// TODO: Use adjacency matrix to speed up this operation.
var node = this.getNode(fromNodeId),
i;
if (!node) {
return null;
}
return null; // no link.
}
};
for (i = 0; i < node.links.length; ++i) {
var link = node.links[i];
if (link.fromId === fromNodeId && link.toId === toNodeId) {
return link;
}
}
// Let graph fire events before we return it to the caller.
var eventify = require('ngraph.events');
eventify(graphPart);
return null; // no link.
}
};
return graphPart;
// Let graph fire events before we return it to the caller.
var eventify = require('ngraph.events');
eventify(graphPart);
function createNodeIterator() {
// Object.keys iterator is 1.3x faster than `for in` loop.
// See `https://github.com/anvaka/ngraph.graph/tree/bench-for-in-vs-obj-keys`
// branch for perf test
return Object.keys ? objectKeysIterator : forInIterator;
}
return graphPart;
function objectKeysIterator(callback) {
if (typeof callback !== 'function') {
return;
}
var keys = Object.keys(nodes);
for (var i = 0; i < keys.length; ++i) {
if (callback(nodes[keys[i]])) {
return; // client doesn't want to proceed. return.
}
}
}
function forInIterator(callback) {
if (typeof callback !== 'function') {
return;
}
var node;
for (node in nodes) {
if (callback(nodes[node])) {
return; // client doesn't want to proceed. return.
}
}
}
};

@@ -385,16 +420,16 @@

function indexOfElementInArray(element, array) {
if (array.indexOf) {
return array.indexOf(element);
}
if (array.indexOf) {
return array.indexOf(element);
}
var len = array.length,
i;
var len = array.length,
i;
for (i = 0; i < len; i += 1) {
if (array[i] === element) {
return i;
}
for (i = 0; i < len; i += 1) {
if (array[i] === element) {
return i;
}
}
return -1;
return -1;
}

@@ -406,5 +441,5 @@

function Node(id) {
this.id = id;
this.links = [];
this.data = null;
this.id = id;
this.links = [];
this.data = null;
}

@@ -417,6 +452,6 @@

function Link(fromId, toId, data, id) {
this.fromId = fromId;
this.toId = toId;
this.data = data;
this.id = id;
this.fromId = fromId;
this.toId = toId;
this.data = data;
this.id = id;
}
{
"name": "ngraph.graph",
"version": "0.0.4",
"version": "0.0.5",
"description": "Base graph structure in ngraph.*",

@@ -5,0 +5,0 @@ "main": "index.js",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc