UNPKG

sequence-graph-data-structure

Version:
257 lines (159 loc) 12.5 kB
# graph-data-structure A [graph data structure](https://en.wikipedia.org/wiki/Graph_(abstract_data_type)) with [topological sort](https://en.wikipedia.org/wiki/Topological_sorting). This library provides a minimalist implementation of a directed graph data structure. Nodes are represented by unique strings. Internally, an [adjacency list](https://en.wikipedia.org/wiki/Adjacency_list) is used to represent nodes and edges. The primary use case for this library is in implementing [dataflow programming](https://en.wikipedia.org/wiki/Dataflow_programming) or [reactive programming](https://en.wikipedia.org/wiki/Reactive_programming). The key algorithm necessary for these is topological sorting, to get an ordering of nodes such that for each edge (**u** -> **v**), **u** comes before **v** in the sorted order. The topological sorting algorithm exposed here has modifications useful for computing the order in which functions in a data flow graph should be executed, namely specifying source nodes for propagation and specifying to exclude the source nodes themselves from the result. **Table of Contents** * [Installing](#installing) * [Examples](#examples) * [ABC](#abc) * [Getting Dressed](#getting-dressed) * [API Reference](#api-reference) ## Installing This library is distributed only via [NPM](npmjs.com). Install by running `npm install graph-data-structure` Require it in your code like this. ```javascript var Graph = require("graph-data-structure"); ``` ## Examples ### ABC To create a graph instance, invoke **[Graph](#graph)** as a constructor function. ```javascript var graph = Graph(); ``` Add some nodes and edges with **[addNode](#add-node)** and **[addEdge](#add-edge)**. ```javascript graph.addNode("a"); graph.addNode("b"); graph.addEdge("a", "b"); ``` Nodes are added implicitly when edges are added. ```javascript graph.addEdge("b", "c"); ``` Now we have the following graph. <img src="https://cloud.githubusercontent.com/assets/68416/15385597/44a10522-1dc0-11e6-9054-2150f851db46.png"> [Topological sorting](https://en.wikipedia.org/wiki/Topological_sorting) can be done by invoking **[topologicalSort](#topological-sort)** like this. ```javascript graph.topologicalSort(); // Returns ["a", "b", "c"] ``` ### Getting Dressed Here's an example of topological sort with getting dressed (from Cormen et al. "Introduction to Algorithms" page 550). <p align="center"> <img src="https://cloud.githubusercontent.com/assets/68416/15385742/36f76410-1dc1-11e6-9fac-a8e41379c795.png"> </p> ```javascript var graph = Graph() .addEdge("socks", "shoes") .addEdge("shirt", "belt") .addEdge("shirt", "tie") .addEdge("tie", "jacket") .addEdge("belt", "jacket") .addEdge("pants", "shoes") .addEdge("underpants", "pants") .addEdge("pants", "belt"); // prints [ "underpants", "pants", "shirt", "tie", "belt", "jacket", "socks", "shoes" ] console.log(graph.topologicalSort()); ``` For more detailed example code that shows more methods, have a look at the [tests](https://github.com/datavis-tech/graph-data-structure/blob/master/test.js). ## API Reference * [Creating a Graph](#creating-a-graph) * [Adding and Removing Nodes](#adding-and-removing-nodes) * [Adding and Removing Edges](#adding-and-removing-edges) * [Querying the Graph](#querying-the-graph) * [Serialization](#serialization) * [Graph Algorithms](#graph-algorithms) ### Creating a Graph <a name="graph" href="#graph">#</a> <b>Graph</b>([<i>serialized</i>]) Constructs an instance of the graph data structure. The optional argument *serialized* is a serialized graph that may have been generated by **[serialize](#serialize)**. If *serialized* is present, it is deserialized by invoking **[deserialize](#deserialize)**. ### Adding and Removing Nodes <a name="add-node" href="#add-node">#</a> <i>graph</i>.<b>addNode</b>(<i>node</i>) Adds a node to the graph. Returns *graph* to support method chaining. The argument *node* is a string identifier that uniquely identifies the node within this graph instance. If a node with the same identifier was already added to the graph, this function does nothing. <a name="remove-node" href="#remove-node">#</a> <i>graph</i>.<b>removeNode</b>(<i>node</i>) Removes the specified node. Returns *graph* to support method chaining. The argument *node* is a string identifier for the node to remove. This function also removes all edges connected to the specified node, both incoming and outgoing. ### Adding and Removing Edges <a name="add-edge" href="#add-edge">#</a> <i>graph</i>.<b>addEdge</b>(<i>u</i>, <i>v</i>[,<i>weight</i>]) Adds an edge from node *u* to node *v*. Returns *graph* to support method chaining. The arguments *u* and *v* are string identifiers for nodes. This function also adds *u* and *v* as nodes if they were not already added. The last argument *weight* (optional) specifies the weight of this edge. <a name="remove-edge" href="#remove-edge">#</a> <i>graph</i>.<b>removeEdge</b>(<i>u</i>, <i>v</i>) Removes the edge from node *u* to node *v*. Returns *graph* to support method chaining. The arguments *u* and *v* are string identifiers for nodes. This function does not remove the nodes *u* and *v*. Does nothing if the edge does not exist. <a name="has-edge" href="#has-edge">#</a> <i>graph</i>.<b>hasEdge</b>(<i>u</i>, <i>v</i>) Returns `true` if there exists an edge from node *u* to node *v*. Returns `false` otherwise. ### Working with Edge Weights <a name="set-edge-weight" href="#set-edge-weight">#</a> <i>graph</i>.<b>setEdgeWeight</b>(<i>u</i>, <i>v</i>, <i>weight</i>) Sets the *weight* (a number) of the edge from node *u* to node *v*. <a name="get-edge-weight" href="#get-edge-weight">#</a> <i>graph</i>.<b>getEdgeWeight</b>(<i>u</i>, <i>v</i>, <i>weight</i>) Gets the *weight* of the edge from node *u* to node *v*. If no weight was previously set on this edge, then the value 1 is returned. ### Querying the Graph <a name="nodes" href="#nodes">#</a> <i>graph</i>.<b>nodes</b>() List all nodes in the graph. Returns an array of node identifier strings. <a name="getEdges" href="#getEdges">#</a> <i>graph</i>.<b>getEdges</b>() List all edges in the graph. Returns an object where the keys are string node IDs for a node and the value is an array of string node IDs. <a name="adjacent" href="#adjacent">#</a> <i>graph</i>.<b>adjacent</b>(<i>node</i>) Gets the adjacent node list for the specified node. The argument *node* is a string identifier for a node. Returns an array of node identifier strings. The "adjacent node list" is the set of nodes for which there is an incoming edge from the given node. In other words, for all edges (**u** -> **v**) where **u** is the specified node, all values for **v** are in the adjacent node list. <a name="indegree" href="#indegree">#</a> <i>graph</i>.<b>indegree</b>(<i>node</i>) Computes the [indegree](https://en.wikipedia.org/wiki/Directed_graph#Indegree_and_outdegree) (number of incoming edges) for the specified *node*. <a name="outdegree" href="#outdegree">#</a> <i>graph</i>.<b>outdegree</b>(<i>node</i>) Computes the [outdegree](https://en.wikipedia.org/wiki/Directed_graph#Indegree_and_outdegree) (number of outgoing edges) for the specified *node*. ### Serialization <a name="serialize" href="#serialize">#</a> <i>graph</i>.<b>serialize</b>() Serializes the graph. Returns an object with the following properties. * `nodes` An array of objects, each with an `id` property whose value is a node identifier string. * `links` An array of objects representing edges, each with the following properties. * `source` The node identifier string of the source node (**u**). * `target` The node identifier string of the target node (**v**). * `weight` The weight of the edge between the source and target nodes. Here's example code for serializing a graph. ```javascript var graph = Graph(); graph.addEdge("a", "b"); graph.addEdge("b", "c"); var serialized = graph.serialize(); ``` The following will be the value of `serialized`. ```json { "nodes": [ { "id": "a" }, { "id": "b" }, { "id": "c" } ], "links": [ { "source": "a", "target": "b", "weight": 1 }, { "source": "b", "target": "c", "weight": 1 } ] } ``` This representation conforms to the convention of graph representation when working with D3.js force layouts. See also [d3.simulation.nodes](https://github.com/d3/d3-force#simulation_nodes) and [d3.forceLinks](https://github.com/d3/d3-force#links). <a name="deserialize" href="#deserialize">#</a> <i>graph</i>.<b>deserialize</b>(<i>serialized</i>) Deserializes the given serialized graph. Returns *graph* to support method chaining. The argument *serialized* is a graph representation with the structure described in **[serialize](#serialize)**. This function iterates over the serialized graph and adds the nodes and links it represents by invoking **[addNode](#add-node)** and **[addEdge](#add-edge)**. The output from **[serialize](#serialize)** can be used as the input to **deserialize**. ### Graph Algorithms <a name="dfs" href="#dfs">#</a> <i>graph</i>.<b>depthFirstSearch</b>([<i>sourceNodes</i>][, <i>includeSourceNodes</i>][, <i>errorOnCycle</i>]) Performs [Depth-first Search](https://en.wikipedia.org/wiki/Depth-first_search). Returns an array of node identifier strings. The returned array includes nodes visited by the algorithm in the order in which they were visited. Implementation inspired by pseudocode from Cormen et al. "Introduction to Algorithms" 3rd Ed. p. 604. Arguments: * *sourceNodes* (optional) - An array of node identifier strings. This specifies the subset of nodes to use as the sources of the depth-first search. If *sourceNodes* is not specified, all **[nodes](#nodes)** in the graph are used as source nodes. * *includeSourceNodes* (optional) - A boolean specifying whether or not to include the source nodes in the returned array. If *includeSourceNodes* is not specified, it is treated as `true` (all source nodes are included in the returned array). * *errorOnCycle* (optional) - A boolean indicating that a `CycleError` should be thrown whenever a cycle is first encountered. Defaults to `false`. <a name="has-cycle" href="#has-cycle">#</a> <i>graph</i>.<b>hasCycle</b>() Checks if the graph has any cycles. Returns `true` if it does and `false` otherwise. <a name="has-cycle" href="#get-cycle">#</a> <i>graph</i>.<b>getCycles</b>() Returns a list of pairs of string node IDs that have a cycle. <a name="walk" href="#walk">#</a> <i>graph</i>.<b>walk</b>(onElement: (node: NodeId) => void) Iterates through each element in the graph at most once. <a name="lca" href="#lca">#</a> <i>graph</i>.<b>lowestCommonAncestors</b>([<i>node1</i>][, <i>node2</i>]) Performs search of [Lowest common ancestors](https://en.wikipedia.org/wiki/Lowest_common_ancestor). Returns an array of node identifier strings. Arguments: * *node1* (required) - First node. * *node2* (required) - Second node. <a name="topological-sort" href="#topological-sort">#</a> <i>graph</i>.<b>topologicalSort</b>([<i>sourceNodes</i>][, <i>includeSourceNodes</i>]) Performs [Topological Sort](https://en.wikipedia.org/wiki/Topological_sorting). Returns an array of node identifier strings. The returned array includes nodes in topologically sorted order. This means that for each visited edge (**u** -> **v**), **u** comes before **v** in the topologically sorted order. Amazingly, this comes from simply reversing the result from depth first search. Inspired by by Cormen et al. "Introduction to Algorithms" 3rd Ed. p. 613. See **[depthFirstSearch](#dfs)** for documentation of the arguments *sourceNodes* and *includeSourceNodes*. Note: this function raises a `CycleError` when the input is not a DAG. <a name="shortest-path" href="#shortest-path">#</a> <i>graph</i>.<b>shortestPath</b>(<i>sourceNode</i>, <i>destinationNode</i>) Performs [Dijkstras Algorithm](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm). Returns an array of node identifier strings. The returned array includes the nodes of the shortest path from source to destination node. The returned array also contains a `weight` property, which is the total weight over all edges in the path. Inspired by by Cormen et al. "Introduction to Algorithms" 3rd Ed. p. 658. <p align="center"> <a href="https://datavis.tech/"> <img src="https://cloud.githubusercontent.com/assets/68416/15298394/a7a0a66a-1bbc-11e6-9636-367bed9165fc.png"> </a> </p>