ngraph.graph

repository·main·Indexed 20 days ago

https://github.com/anvaka/ngraph.graph

A lightweight and efficient graph data structure implementation for JavaScript (version 20.1.2). It supports the creation, growth, and enumeration of nodes and links with arbitrary data association. Key features include event-driven updates via a 'changed' event, bulk update optimization using beginUpdate() and endUpdate(), and support for both standard graphs and multigraphs.

Tokens
4.4K
Snippets
14
Records
16
Agent score
68%

What's inside ngraph.graph

  1. Create and grow a graph

    main

    To start, call createGraph() to initialize an empty graph. You can grow the graph by adding nodes or links.

    Adding Nodes: Use addNode(id, [data]) to add a single node. The id is typically a string or number.

    Adding Links: Use addLink(source, target, [data]) to connect two nodes. If the nodes specified by source and target do not exist in the graph, addLink() will automatically create them.

    Removing Elements:

    • Use removeNode(id) to remove a node.
    • Use removeLink(link) to remove a link. Note that this method requires the actual link object, not just the IDs.
    • Use clear() to remove all nodes and links from the graph.
    var createGraph = require('ngraph.graph');
    var g = createGraph();
    
    // Adding nodes
    g.addNode('hello');
    g.addNode('world');
    
    // Adding links (creates nodes if they don't exist)
    g.addLink('space', 'bar');
    
    // Connecting existing nodes
    g.addLink('hello', 'world');
    
    // Removing
    g.removeNode('space');
    
    // Removing a link requires the link object
    g.forEachLinkedNode('hello', function(linkedNode, link) {
      g.removeLink(link);
    });
    
    // Clear everything
    g.clear();
  2. Install ngraph.graph

    main

    You can install ngraph.graph via npm, use a CDN for browser-based projects, or import it as an ES module.

    NPM:

    npm install ngraph.graph

    CDN: If you use the CDN, the library is available under the global name createGraph.

    <script src='https://unpkg.com/ngraph.graph@20.1.0/dist/ngraph.graph.umd.js'></script>

    ES Modules:

    import createGraph from 'ngraph.graph';
    const g = createGraph();
  3. Handle graph change events

    main

    The graph object supports an event system via on(eventName, callback).

    Optimization: Lazy Event Recording

    To save memory, the graph does not record changes unless there is at least one subscriber (someone has called .on()).

    Bulk Updates

    To perform multiple changes without triggering individual events for every single operation, use beginUpdate() and endUpdate(). This wraps changes into a single 'changed' event.

    • beginUpdate(): Suspends notifications.
    • endUpdate(): Resumes notifications and fires a single 'changed' event containing an array of all accumulated changes.

    Change Object Format

    Each change in the 'changed' event array is an object:

    • changeType: One of 'add', 'remove', or 'update'.
    • node: The node object (if the change was node-related).
    • link: The link object (if the change was link-related).
    var graph = require('ngraph.graph')();
    
    // Subscribe to changes
    graph.on('changed', function(changes) {
      changes.forEach(function(change) {
        if (change.node) {
          console.log('Node change:', change.node.id, change.changeType);
        } else if (change.link) {
          console.log('Link change:', change.link.id, change.changeType);
        }
      });
    });
    
    // Perform bulk updates
    graph.beginUpdate();
    graph.addNode(1);
    graph.addNode(2);
    graph.addLink(1, 2);
    graph.endUpdate(); // Fires one 'changed' event
  4. Listen to graph change events

    main

    The graph emits a changed event whenever it is modified. You can listen to these changes using .on() and stop listening using .off().

    Change Records: The callback for the changed event receives an array of ChangeRecord objects. Each record contains:

    • changeType: 'add', 'remove', or 'update'.
    • node: The node object (only present for node changes).
    • link: The link object (only present for link changes).

    Bulk Updates: To prevent multiple event triggers during large operations, wrap your changes in beginUpdate() and endUpdate(). The changed event will only fire once after endUpdate() is called.

    Example:

    // Listen for changes
    g.on('changed', function(changes) {
      console.dir(changes);
    });
    
    // Perform bulk updates to avoid event spam
    g.beginUpdate();
    for(var i = 0; i < 100; ++i) {
      g.addLink(i, i + 1);
    }
    g.endUpdate(); // Triggers 'changed' once
    
    // Stop listening
    g.off('changed', handler);
    g.on('changed', function(changes) {
      console.dir(changes);
    });
    
    // Bulk update pattern
    g.beginUpdate();
    // ... many operations ...
    g.endUpdate();
    
    // Unsubscribe
    g.off('changed', myHandler);
  5. Enumerate nodes and links

    main

    Use the following methods to iterate over the graph's contents:

    • forEachNode(callback): Iterates over all nodes. The callback receives a node object. Use node.id and node.data to access the ID and associated data.
    • forEachLink(callback): Iterates over all links. The callback receives a link object.
    • forEachLinkedNode(id, callback, [outboundOnly]): Iterates over nodes connected to the specified id.
      • By default, it iterates over both inbound and outbound links.
      • Pass true as the third argument to iterate only over outbound links.
    • getNode(id): Returns the node object for the given ID.
    • getLink(source, target): Returns the link object between the two nodes.
    // Iterate nodes
    g.forEachNode(function(node) {
        console.log(node.id, node.data);
    });
    
    // Iterate all links
    g.forEachLink(function(link) {
        console.dir(link);
    });
    
    // Iterate linked nodes (both inbound and outbound)
    g.forEachLinkedNode('hello', function(linkedNode, link) {
        console.log("Connected node: ", linkedNode.id, linkedNode.data);
        console.dir(link);
    });
    
    // Iterate ONLY outbound links
    g.forEachLinkedNode('hello', function(linkedNode, link) {
        /* ... */
    }, true);
    
    // Get specific objects
    var node = g.getNode('world');
    var link = g.getLink('hello', 'world');
  6. Associate data with nodes and links

    main

    You can attach arbitrary data to nodes and links to store metadata.

    Node Data: Pass data as the second argument to addNode(id, data). You can retrieve it via the data property on the node object returned by getNode(id).

    Link Data: Pass data as the third argument to addLink(source, target, data).

    Example:

    // Associate an object with a node
    g.addNode('server', {
      status: 'on',
      ip: '127.0.0.1'
    });
    
    // Retrieve the data
    var server = g.getNode('server');
    console.log(server.data); // { status: 'on', ip: '127.0.0.1' }
    
    // Associate an object with a link
    const x = { weight: 10 };
    g.addLink(1, 2, x);
    g.addNode('server', {
      status: 'on',
      ip: '127.0.0.1'
    });
    
    var server = g.getNode('server');
    console.log(server.data);
    
    // Link data
    g.addLink(1, 2, { weight: 10 });
  7. Manage nodes in ngraph.graph

    main

    Use the following methods to manipulate nodes within the graph:

    • addNode(nodeId, [data]): Adds a node with the given nodeId. If the node already exists, its data is updated/augmented. Returns the node object.
    • getNode(nodeId): Returns the node object for the given nodeId, or undefined if it doesn't exist.
    • removeNode(nodeId): Removes the node and all its incident links. Returns true if the node was removed, false otherwise.
    • hasNode(nodeId): Returns the node object if it exists (truthy), otherwise falsy. (Synonym for getNode).
    • getNodeCount(): Returns the total number of nodes. (Synonym for getNodesCount).
    • clear(): Removes all nodes and links from the graph.

    Node objects contain id, data, and a links Set.

    var graph = require('ngraph.graph')();
    
    // Add a node
    var node = graph.addNode(1, { label: 'start' });
    
    // Get a node
    var node = graph.getNode(1);
    
    // Check existence
    if (graph.hasNode(1)) { /* ... */ }
    
    // Remove a node
    graph.removeNode(1);
  8. Manage links in ngraph.graph

    main

    Use the following methods to manipulate links within the graph:

    • addLink(fromId, toId, [data]): Creates a link between fromId and toId. If either node does not exist, it is automatically created. Returns the new link object.
    • removeLink(link, [otherId]): Removes a link. If otherId is provided, it uses getLink(link, otherId) to find the specific link. Returns true if removed, false otherwise.
    • getLink(fromId, toId): Returns the link between the two IDs, or undefined if none exists. (Synonym for hasLink).
    • getLinkById(linkId): Returns the link associated with the specific linkId.
    • getLinkCount(): Returns the total number of links. (Synonyms: getEdgeCount, getLinksCount).
    • hasLink(fromId, toId): Returns the link if it exists, otherwise null. (Synonym for getLink).

    Link objects contain fromId, toId, data, and id.

    var graph = require('ngraph.graph')();
    
    // Add a link (automatically creates nodes if they don't exist)
    var link = graph.addLink(1, 2, { weight: 10 });
    
    // Get a link
    var link = graph.getLink(1, 2);
    
    // Remove a link
    graph.removeLink(link);
  9. Get graph statistics and counts

    main

    The following methods provide information about the graph size:

    • getNodeCount() or getNodesCount(): Returns the number of nodes.
    • getLinkCount(), getEdgeCount(), or getLinksCount(): Returns the number of links/edges.
  10. Batch updates with beginUpdate and endUpdate

    main

    To prevent excessive event notifications during bulk operations, wrap multiple changes in beginUpdate() and endUpdate().

    • beginUpdate(): Suspends all notifications about graph changes.
    • endUpdate(): Resumes notifications and fires a single 'changed' event if any changes occurred during the suspended period.
    graph.beginUpdate();
    
    for (let i = 0; i < 100; i++) {
      graph.addNode(i);
    }
    
    graph.endUpdate(); // Fires one 'changed' event
  11. Manage links (edges) in a graph

    main

    Links connect two nodes via fromId and toId.

    • addLink(fromId, toId, data?): Adds a link. In a non-multigraph, this overwrites data for existing links between these nodes.
    • removeLink(link): Removes a specific Link instance.
    • removeLink(fromId, toId): Removes the link between two node IDs.
    • getLink(fromId, toId): Returns the Link object or undefined.
    • getLinkById(linkId): Returns a link by its unique LinkId (string).
    • hasLink(fromId, toId): Returns the link or undefined.
    • forEachLink(callback): Iterates over all links. Returning a truthy value stops iteration.
    const graph = createGraph<string, string>();
    
    graph.addNode('A');
    graph.addNode('B');
    const link = graph.addLink('A', 'B', 'connection data');
    
    const existingLink = graph.getLink('A', 'B');
    const linkById = graph.getLinkById(link.id);
    
    graph.removeLink('A', 'B');
  12. Iterate over nodes and links

    main

    The graph provides several methods for traversing its structure:

    • forEachNode(callback): Invokes callback(node) for every node. Returning true from the callback stops iteration.
    • forEachLink(callback): Invokes callback(link) for every link. Returning true from the callback stops iteration.
    • getLinks(nodeId): Returns a Set of all links (inbound and outbound) incident to the node. Returns null if the node is not found.
    • forEachLinkedNode(nodeId, callback, [oriented]): Invokes callback(node, link) for all nodes adjacent to the specified nodeId.
      • If oriented is true, it only iterates over outbound links (link.fromId === nodeId).
      • If oriented is false (default), it iterates over both inbound and outbound links.
      • Returning true from the callback stops iteration.
    // Iterate all nodes
    graph.forEachNode(function(node) {
      console.log('Node:', node.id);
    });
    
    // Iterate adjacent nodes (non-oriented)
    graph.forEachLinkedNode(1, function(adjacentNode, link) {
      console.log('Adjacent to 1:', adjacentNode.id, 'via', link.id);
    });