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 19.1.0 to 20.0.0

.eslintignore

4

examples/findConnectedComponents.js

@@ -25,3 +25,3 @@ /**

dfs(graph, node.id, otherNode => {
let componentId = nodeIdToComponentId.get(otherNode.id)
let componentId = nodeIdToComponentId.get(otherNode.id);
if (componentId !== undefined && componentId === lastComponentId) {

@@ -44,3 +44,3 @@ // this is possible when we have a loop. Just ignore the node.

return connectedComponents;
}
};

@@ -47,0 +47,0 @@ function dfs(graph, startFromNodeId, visitor) {

@@ -1,6 +0,9 @@

// Type definitions for ngraph.graph v18.0.0
// Type definitions for ngraph.graph v20.0.0
// Project: https://github.com/anvaka/ngraph.graph
// Definitions by: Nathan Westlake <https://github.com/CorayThan>
// Definitions by: Nathan Westlake <https://github.com/CorayThan> and Anvaka <https://github.com/anvaka>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
/**
* A graph data structure for javascript.
*/
declare module "ngraph.graph" {

@@ -12,40 +15,169 @@ import { EventedType } from 'ngraph.events'

/**
* A single link (edge) of the graph
*/
export interface Link<Data = any> {
/**
* Unique identifier of this link
*/
id: LinkId,
/**
* Node identifer where this links starts
*/
fromId: NodeId,
/**
* Node identifer where this link points to
*/
toId: NodeId,
/**
* Arbitrary data associated with this link
*/
data: Data
}
/**
* A single node of a graph.
*/
export interface Node<Data = any> {
/**
* Unique identifier of this node
*/
id: NodeId,
links: Link[],
/**
* Set of incoming/outgoing links (edges) to/from this node.
*
* For the sake of memory consumption preservation, this property
* is null when this node has no links.
*
* Link instance is referentially equal throughout the API.
*/
links: Set<Link<any>> | null,
/**
* Associated data connected to this node.
*/
data: Data
}
/**
* A graph data structure
*/
export interface Graph<NodeData = any, LinkData = any> {
/**
* Adds a new node to the graph. If node with such id already exists
* its data is overwritten with the new data
*/
addNode: (node: NodeId, data?: NodeData) => Node<NodeData>
/**
* Adds a new link to the graph. If link already exists and the graph
* is not a multigraph, then link's data is overwritten with a new data.
*
* When graph is a multigraph, then a new link is always added between the
* nodes.
*/
addLink: (from: NodeId, to: NodeId, data?: LinkData) => Link<LinkData>
/**
* Removes a link from the graph. You'll need to pass an actual link instance
* to remove it. If you pass two arguments, the function assumes they represent
* from/to node ids, and removes the corresponding link.
*
* Returns true if link is found and removed. False otherwise.
*/
removeLink: (link: Link<LinkData>) => boolean
/**
* Removes node by node id. Returns true if node was removed,
* false otherwise (e.g. no such node exists in the graph)
*/
removeNode: (nodeId: NodeId) => boolean
/**
* Returns a node by its identifier. Undefined value is returned if node
* with such identifer does not exist.
*/
getNode: (nodeId: NodeId) => Node<NodeData> | undefined
/**
* Checks whether given node exists in the graph. Return the node
* or undefined if no such node exist.
*/
hasNode: (nodeId: NodeId) => Node<NodeData> | undefined
getLink: (fromNodeId: NodeId, toNodeId: NodeId) => Link<LinkData> | null
hasLink: (fromNodeId: NodeId, toNodeId: NodeId) => Link<LinkData> | null
/**
* Returns a link between two nodes
*/
getLink: (fromNodeId: NodeId, toNodeId: NodeId) => Link<LinkData> | undefined
/**
* Checks if link is present in the graph
*/
hasLink: (fromNodeId: NodeId, toNodeId: NodeId) => Link<LinkData> | undefined
/**
* Returns number of nodes in the graph
*/
getNodesCount: () => number
/**
* Returns number of nodes in the graph
*/
getNodeCount: () => number
/**
* Returns number of links (edges) in the graph
*/
getLinksCount: () => number
getNodeCount: () => number
/**
* Returns number of links (edges) in the graph
*/
getLinkCount: () => number
getLinks: (nodeId: NodeId) => Link<LinkData>[] | null
/** To stop the iteration return true in the callback */
/**
* Returns all links associated with this node
*/
getLinks: (nodeId: NodeId) => Set<Link<LinkData>> | null
/**
* Iterates over every single node in the graph, passing the node to a callback.
*
* If callback function returns "true"-like value, enumeration stops.
**/
forEachNode: (callbackPerNode: (node: Node<NodeData>) => void | undefined | null | boolean) => void
/**
* Iterates over every single link in the graph, passing the link to a callback.
* If callback function returns "true"-like value, enumeration stops.
*/
forEachLink: (callbackPerLink: (link: Link<LinkData>) => void | undefined | boolean) => void
forEachLinkedNode: (nodeId: NodeId, callbackPerNode: (node: Node<NodeData>, link: Link<LinkData>) => void, oriented: boolean) => void
forEachLink: (callbackPerLink: (link: Link<LinkData>) => void) => void
/**
* Suspend all notifications about graph changes until
* endUpdate is called.
*/
beginUpdate: () => void
/**
* Resumes all notifications about graph changes and fires
* graph 'changed' event in case there are any pending changes.
*/
endUpdate: () => void
/**
* Removes all nodes and links from the graph.
*/
clear: () => void
}
/**
* Creates a new instance of a graph.
*/
export default function createGraph<NodeData = any, LinkData = any>(options?: { multigraph: boolean }): Graph<NodeData, LinkData> & EventedType
}

@@ -52,9 +52,9 @@ /**

var nodes = new Map();
var links = [],
var nodes = new Map(); // nodeId => Node
var links = new Map(); // linkId => Link
// Hash of multi-edges. Used to track ids of edges between same nodes
multiEdges = {},
suspendEvents = 0,
var multiEdges = {};
var suspendEvents = 0;
createLink = options.multigraph ? createUniqueLink : createSingleLink,
var createLink = options.multigraph ? createUniqueLink : createSingleLink,

@@ -81,2 +81,8 @@ // Our graph API provides means to listen to graph changes. Users can subscribe

/**
* Sometimes duck typing could be slow. Giving clients a hint about data structure
* via explicit version number here:
*/
version: 20.0,
/**
* Adds node to the graph. If node with given id already exists in the graph

@@ -147,2 +153,7 @@ * its data is extended with whatever comes in 'data' argument.

/**
* Gets total number of links in the graph.
*/
getEdgeCount: getLinkCount,
/**
* Synonym for `getLinkCount()`

@@ -163,3 +174,3 @@ */

*
* @return Array of links from and to requested node if such node exists;
* @return Set of links from and to requested node if such node exists;
* otherwise null is returned.

@@ -220,3 +231,3 @@ */

* Operation complexity is O(n) where n - number of links of a node.
* NOTE: this function is synonim for getLink()
* NOTE: this function is synonym for getLink()
*

@@ -231,3 +242,3 @@ * @returns link if there is one. null otherwise.

* Operation complexity is O(1)
* NOTE: this function is synonim for getNode()
* NOTE: this function is synonym for getNode()
*

@@ -245,3 +256,3 @@ * @returns node if there is one; Falsy value otherwise.

*
* @returns link if there is one. null otherwise.
* @returns link if there is one; undefined otherwise.
*/

@@ -329,9 +340,7 @@ getLink: getLink

if (prevLinks) {
prevLinks.forEach(removeLinkInstance);
node.links = null;
for(var i = 0; i < prevLinks.length; ++i) {
removeLink(prevLinks[i]);
}
}
nodes.delete(nodeId)
nodes.delete(nodeId);

@@ -353,4 +362,5 @@ recordNodeChange(node, 'remove');

var link = createLink(fromId, toId, data);
var isUpdate = links.has(link.id);
links.push(link);
links.set(link.id, link);

@@ -364,3 +374,3 @@ // TODO: this is not cool. On large graphs potentially would consume more memory.

recordLinkChange(link, 'add');
recordLinkChange(link, isUpdate ? 'update' : 'add');

@@ -374,2 +384,8 @@ exitModification();

var linkId = makeLinkId(fromId, toId);
var prevLink = links.get(linkId);
if (prevLink) {
prevLink.data = data;
return prevLink;
}
return new Link(fromId, toId, data, linkId);

@@ -379,3 +395,3 @@ }

function createUniqueLink(fromId, toId, data) {
// TODO: Get rid of this method.
// TODO: Find a better/faster way to store multigraphs
var linkId = makeLinkId(fromId, toId);

@@ -399,3 +415,3 @@ var isMultiEdge = multiEdges.hasOwnProperty(linkId);

function getLinkCount() {
return links.length;
return links.size;
}

@@ -408,14 +424,18 @@

function removeLink(link) {
function removeLink(link, otherId) {
if (otherId !== undefined) {
link = getLink(link, otherId);
}
return removeLinkInstance(link);
}
function removeLinkInstance(link) {
if (!link) {
return false;
}
var idx = indexOfElementInArray(link, links);
if (idx < 0) {
return false;
}
if (!links.get(link.id)) return false;
enterModification();
links.splice(idx, 1);
links.delete(link.id);

@@ -426,13 +446,7 @@ var fromNode = getNode(link.fromId);

if (fromNode) {
idx = indexOfElementInArray(link, fromNode.links);
if (idx >= 0) {
fromNode.links.splice(idx, 1);
}
fromNode.links.delete(link);
}
if (toNode) {
idx = indexOfElementInArray(link, toNode.links);
if (idx >= 0) {
toNode.links.splice(idx, 1);
}
toNode.links.delete(link);
}

@@ -448,17 +462,4 @@

function getLink(fromNodeId, toNodeId) {
// TODO: Use sorted links to speed this up
var node = getNode(fromNodeId),
i;
if (!node || !node.links) {
return null;
}
for (i = 0; i < node.links.length; ++i) {
var link = node.links[i];
if (link.fromId === fromNodeId && link.toId === toNodeId) {
return link;
}
}
return null; // no link.
if (fromNodeId === undefined || toNodeId === undefined) return undefined;
return links.get(makeLinkId(fromNodeId, toNodeId));
}

@@ -475,6 +476,10 @@

function forEachLink(callback) {
var i, length;
if (typeof callback === 'function') {
for (i = 0, length = links.length; i < length; ++i) {
callback(links[i]);
var valuesIterator = links.values();
var nextValue = valuesIterator.next();
while (!nextValue.done) {
if (callback(nextValue.value)) {
return true; // client doesn't want to proceed. Return.
}
nextValue = valuesIterator.next();
}

@@ -496,8 +501,11 @@ }

// eslint-disable-next-line no-shadow
function forEachNonOrientedLink(links, nodeId, callback) {
var quitFast;
for (var i = 0; i < links.length; ++i) {
var link = links[i];
var valuesIterator = links.values();
var nextValue = valuesIterator.next();
while (!nextValue.done) {
var link = nextValue.value;
var linkedNodeId = link.fromId === nodeId ? link.toId : link.fromId;
quitFast = callback(nodes.get(linkedNodeId), link);

@@ -507,11 +515,15 @@ if (quitFast) {

}
nextValue = valuesIterator.next();
}
}
// eslint-disable-next-line no-shadow
function forEachOrientedLink(links, nodeId, callback) {
var quitFast;
for (var i = 0; i < links.length; ++i) {
var link = links[i];
var valuesIterator = links.values();
var nextValue = valuesIterator.next();
while (!nextValue.done) {
var link = nextValue.value;
if (link.fromId === nodeId) {
quitFast = callback(nodes.get(link.toId), link)
quitFast = callback(nodes.get(link.toId), link);
if (quitFast) {

@@ -521,2 +533,3 @@ return true; // Client does not need more iterations. Break now.

}
nextValue = valuesIterator.next();
}

@@ -558,22 +571,2 @@ }

// need this for old browsers. Should this be a separate module?
function indexOfElementInArray(element, array) {
if (!array) return -1;
if (array.indexOf) {
return array.indexOf(element);
}
var len = array.length,
i;
for (i = 0; i < len; i += 1) {
if (array[i] === element) {
return i;
}
}
return -1;
}
/**

@@ -590,5 +583,5 @@ * Internal structure to represent node;

if (node.links) {
node.links.push(link);
node.links.add(link);
} else {
node.links = [link];
node.links = new Set([link]);
}

@@ -595,0 +588,0 @@ }

{
"name": "ngraph.graph",
"version": "19.1.0",
"version": "20.0.0",
"description": "graph data structure",

@@ -10,4 +10,4 @@ "main": "index.js",

"build": "browserify index.js -s createGraph -o dist/ngraph.graph.js && uglifyjs dist/ngraph.graph.js -o dist/ngraph.graph.min.js",
"test": "tap test/*.js",
"cover": "tap --coverage-report=html --no-browser test/*.js"
"test": "tap --branches=90 --lines=90 --statements=90 --functions=90 test/*.js",
"cover": "tap --branches=80 --lines=80 --statements=80 --functions=80 --coverage-report=html --no-browser test/*.js"
},

@@ -37,6 +37,7 @@ "repository": {

"benchmark": "^2.1.4",
"browserify": "^16.5.0",
"ngraph.random": "^1.0.0",
"tap": "^14.10.7",
"uglify-js": "^3.8.0"
"browserify": "^17.0.0",
"eslint": "^7.25.0",
"ngraph.random": "^1.1.0",
"tap": "^15.0.9",
"uglify-js": "^3.13.5"
},

@@ -43,0 +44,0 @@ "dependencies": {

var createGraph = require('../');
var randomAPI = require('ngraph.random').random
var randomAPI = require('ngraph.random').random;
var Benchmark = require('benchmark');

@@ -4,0 +4,0 @@ var suite = new Benchmark.Suite;

var createGraph = require('../');
var randomAPI = require('ngraph.random').random
var randomAPI = require('ngraph.random').random;
var Benchmark = require('benchmark');

@@ -40,3 +40,3 @@ var suite = new Benchmark.Suite;

}
})
});

@@ -75,3 +75,3 @@ suite.add('Adding 5,000 random edges and randomly removing them', function() {

}
})
});

@@ -96,3 +96,3 @@ suite.add('Removing all edges one by one from 5k graph', function() {

}
})
});

@@ -117,3 +117,3 @@ suite.add('Removing all edges one by one from 5k multigraph graph', function() {

}
})
});

@@ -120,0 +120,0 @@ suite.on('cycle', function(event) {

@@ -7,4 +7,21 @@ ngraph.graph

[![build status](https://secure.travis-ci.org/anvaka/ngraph.graph.png)](http://travis-ci.org/anvaka/ngraph.graph)
[![build status](https://github.com/anvaka/ngraph.graph/actions/workflows/tests.yaml/badge.svg)](https://github.com/anvaka/ngraph.graph/actions/workflows/tests.yaml)
Install
=======
With [npm](http://npmjs.org) do:
```
npm install ngraph.graph
```
Or download from CDN:
``` html
<script src='https://unpkg.com/ngraph.graph@19.0.0/dist/ngraph.graph.min.js'></script>
```
If you download from CDN the library will be available under `createGraph` global name.
## Creating a graph

@@ -179,21 +196,5 @@ Create a graph with no edges and no nodes:

Install
=======
With [npm](http://npmjs.org) do:
```
npm install ngraph.graph
```
Or download from CDN:
``` html
<script src='https://unpkg.com/ngraph.graph@19.0.0/dist/ngraph.graph.min.js'></script>
```
If you download from CDN the library will be available under `createGraph` global name.
License
=======
BSD 3-clause

@@ -48,6 +48,13 @@ var test = require('tap').test,

t.equals(graph.getLink, graph.hasLink, 'hasLink is synonim for getLink');
t.equal(graph.getLink, graph.hasLink, 'hasLink is synonym for getLink');
t.end();
});
test('it considers uniqueLinkId as multigraph', function (t) {
var options = {uniqueLinkId: true};
createGraph(options);
t.equal(options.multigraph, true, 'multigraph is set');
t.end();
});
test('it throw if no node id is passed', function(t) {

@@ -73,5 +80,5 @@ var graph = createGraph();

var change = changes[0];
t.equals(change.node.id, 1);
t.equals(change.node.data, 'world');
t.equals(change.changeType, 'update');
t.equal(change.node.id, 1);
t.equal(change.node.data, 'world');
t.equal(change.changeType, 'update');
}

@@ -91,4 +98,4 @@ });

t.ok(graph.hasLink('watch', 'constructor'));
t.equals(graph.getLinksCount(), 1, 'one link');
t.equals(iterated, 2, 'has two nodes');
t.equal(graph.getLinksCount(), 1, 'one link');
t.equal(iterated, 2, 'has two nodes');
t.end();

@@ -106,6 +113,6 @@ });

t.equal(graph.getLinksCount(), 1, 'One link');
t.equal(firstNodeLinks.length, 1, 'number of links of the first node is wrong');
t.equal(secondNodeLinks.length, 1, 'number of links of the second node is wrong');
t.equal(link, firstNodeLinks[0], 'invalid link in the first node');
t.equal(link, secondNodeLinks[0], 'invalid link in the second node');
t.equal(firstNodeLinks.size, 1, 'number of links of the first node is wrong');
t.equal(secondNodeLinks.size, 1, 'number of links of the second node is wrong');
t.equal(link, Array.from(firstNodeLinks)[0], 'invalid link in the first node');
t.equal(link, Array.from(secondNodeLinks)[0], 'invalid link in the second node');
t.end();

@@ -132,3 +139,4 @@ });

test('it can produce unique link ids', function (t) {
t.test('by default links are not unique', function (t) {
// eslint-disable-next-line no-shadow
t.test('by default links are de-duped', function (t) {
var seen = {};

@@ -142,4 +150,6 @@ var graph = createGraph();

var link = graph.getLink(1, 2);
t.equals(seen[link.id], 3, 'Link 1->2 seen 3 times')
t.equal(seen[link.id], 1, 'Link 1->2 seen 1 time');
t.equal(link.data, 'third', 'Last link wins');
// eslint-disable-next-line no-shadow
function verifyLinksAreNotUnique(link) {

@@ -151,2 +161,3 @@ seen[link.id] = (seen[link.id] || 0) + 1;

// eslint-disable-next-line no-shadow
t.test('You can create multigraph', function (t) {

@@ -162,3 +173,3 @@ var graph = createGraph({

graph.forEachLink(verifyLinkIsUnique);
t.equals(graph.getLinksCount(), 3, 'All three links are here');
t.equal(graph.getLinksCount(), 3, 'All three links are here');
t.end();

@@ -172,19 +183,2 @@

t.test('you can sacrifice uniqueness in favor of performance', function (t) {
var graph = createGraph({ });
var seen = {};
graph.addLink(1, 2, 'first');
graph.addLink(1, 2, 'second');
graph.addLink(1, 2, 'third');
graph.forEachLink(noticeLink);
t.equals(Object.keys(seen).length, 1, 'Only one link id');
t.end();
function noticeLink(link) {
seen[link.id] = true;
}
});
t.end();

@@ -260,4 +254,4 @@ });

t.equal(graph.getLinksCount(), 0, 'No Links');
t.equal(graph.getLinks(1).length, 0, 'link should be removed from the first node');
t.equal(graph.getLinks(2).length, 0, 'link should be removed from the second node');
t.equal(graph.getLinks(1).size, 0, 'link should be removed from the first node');
t.equal(graph.getLinks(2).size, 0, 'link should be removed from the second node');
t.ok(linkIsRemoved, 'Link removal is successful');

@@ -272,2 +266,21 @@

test('it can remove link by from/to ids', function(t) {
var graph = createGraph();
graph.addLink(1, 2);
var linkIsRemoved = graph.removeLink(1, 2);
t.equal(graph.getNodesCount(), 2, 'remove link should not remove nodes');
t.equal(graph.getLinksCount(), 0, 'No Links');
t.equal(graph.getLinks(1).size, 0, 'link should be removed from the first node');
t.equal(graph.getLinks(2).size, 0, 'link should be removed from the second node');
t.ok(linkIsRemoved, 'Link removal is successful');
graph.forEachLink(function() {
test.ok(false, 'No links should be in graph');
});
t.end();
});
test('remove link returns false if no link removed', function (t) {

@@ -343,4 +356,4 @@ var graph = createGraph();

t.equal(graph.getLinks(1), null, 'link should be removed from the first node');
t.equal(graph.getLinks(2).length, 0, 'link should be removed from the second node');
t.equal(graph.getLinks(3).length, 0, 'link should be removed from the third node');
t.equal(graph.getLinks(2).size, 0, 'link should be removed from the second node');
t.equal(graph.getLinks(3).size, 0, 'link should be removed from the third node');
graph.forEachLink(function() {

@@ -380,6 +393,6 @@ test.ok(false, 'No links should be in graph');

graph.addNode(1);
t.equals(changedCount, 0, 'Begin update freezes updates until `endUpdate()`');
t.equal(changedCount, 0, 'Begin update freezes updates until `endUpdate()`');
graph.endUpdate();
t.equals(changedCount, 1, 'event is fired only after endUpdate()');
t.equal(changedCount, 1, 'event is fired only after endUpdate()');
t.end();
});

@@ -19,8 +19,8 @@ var test = require('tap').test;

t.equal(components.length, 4, 'all components found');
t.deepEqual(components[0], [1, 2, 3], 'first component found')
t.deepEqual(components[1], [5, 6], 'second component found')
t.deepEqual(components[2], [8], 'third component found')
t.deepEqual(components[3], [9], 'fourth component found')
t.same(components[0], [1, 2, 3], 'first component found');
t.same(components[1], [5, 6], 'second component found');
t.same(components[2], [8], 'third component found');
t.same(components[3], [9], 'fourth component found');
t.end();
});

@@ -45,3 +45,2 @@ var test = require('tap').test,

test('forEachLinkedNode can quit fast for oriented graphs', function(t) {
t.plan(1);
var graph = createGraph();

@@ -52,10 +51,13 @@ var oriented = true;

var visited = 0;
graph.forEachLinkedNode(1, function() {
t.ok(true, 'Visited first link');
visited += 1;
return true; // We want to stop right now!
}, oriented);
t.equal(visited, 1, 'One link is visited');
t.end();
});
test('forEachLinkedNode can quit fast for non-oriented graphs', function(t) {
t.plan(1);
var graph = createGraph();

@@ -66,6 +68,11 @@ var oriented = false;

var visited = 0;
graph.forEachLinkedNode(1, function() {
t.ok(true, 'Visited first link');
visited += 1;
return true; // We want to stop right now!
}, oriented);
t.equal(visited, 1, 'One link is visited');
t.end();
});

@@ -111,3 +118,3 @@

graph.forEachNode(function(node) {
t.equals(node.id, 1, 'We visit only one node');
t.equal(node.id, 1, 'We visit only one node');
return true; // we want to stop now!

@@ -123,3 +130,3 @@ });

var fastQuit = graph.forEachNode(function(node) {
t.equals(node.id, 1, 'We visit only one node');
t.equal(node.id, 1, 'We visit only one node');
return true; // we want to stop now!

@@ -134,1 +141,24 @@ }); // no callback? No worries

});
test('forEachNode throws when callback is not a function', function(t) {
var graph = createGraph();
graph.addLink(1, 2);
t.throws(function() {
graph.forEachNode('not a function');
});
t.end();
});
test('forEachLink stops when requested', function(t) {
var graph = createGraph();
graph.addLink(1, 2);
graph.addLink(2, 3);
var visitCount = 0;
graph.forEachLink(function() {
visitCount += 1;
return true;
});
t.equal(visitCount, 1, 'only one link visited');
t.end();
});

Sorry, the diff of this file is not supported yet

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