npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

shortest-way

v1.1.2

Published

Implementation of the Dijkstra's Algorithm to compute the shortest path between a starting and ending node ina graph.

Downloads

9

Readme

Build Status

About

Tihs module is an implementation of the Dijkstra's Algorithm to compute the shortest path between a starting and ending node ina graph

API

We provide one class Node to create a node, that connect to other nodes and which have the compute method to compute the shortest path from the starting node till the ending node.

You load it like this

var Node = require( 'shortest-way' ).Node;

Construcor

The constructor takes one parameter as an object:

Node( object )
  • object is an object with 3 properties
    • id is the mandatory unique identifier of the node.
    • offset is an offset to add for each next node added. See the method addNext. It is optional.

For example :

var Node = require( 'shortest-way' ).Node;
var start = new Node( { id:'start' } );
start.raw = { cityName: 'Paris'};

Building the graph

The node class has a method to add a next node. Hence you build a directional relation between 2 nodes.

node.addNode( nextNode, weight )

This method takes 2 parameters:

  • nextNode is the next node to add to this node
  • weight is the weight of the relation between this node and the next node. It is
  • a positive number and its detault value is 0. You can concider
    • it as the distance between 2 nodes
    • or the time it takes between 2 nodes.

An error is raised if

  • the next node is already was already added to this node
  • the weight is negative

Example:

var Node = require( 'shortest-way' ).Node;

// create nodes
var startNode = new Node( { id: 'start' } );
var middleNode = new Node( { id: 'middle' } );
var endNode = new Node( { id: 'end' } );

// add relation
startNode.addNext( middleNode, 1 );
middleNode.addNext( endNode, 1 );

compute the shortest path

Once all nodes are created and linked together with the method addNext, you can call the compute method on the starting node.

var path = startNode.compute( endNode, allNodes )

The arguments are :

  • endNode is the destination for the shortest path you want to compute
  • allNodes can be
    • an array containing all the nodes of the graph
    • an object containing all nodes of the graph.
var startNode = new Node( { id: 'start' } );
var middleNode = new Node( { id: 'middle' } );
var endNode = new Node( { id: 'end' } );

// all nodes stored into an array
var allNodes = [ statNode, middleNode, endNode ];

var result = startNode.compute( endNode, allNodes );

// OR all nodes stored into an object
allNodes = {
  'start': startNode,
  'middle': middleNode,
  'end': endNode
};

var result = startNode.compute( endNode, allNodes );

Both format for allNodes are supported. If allNodes is an array, then it will be converted once into an object before computing the shortest path because it if faster to compute with an object containing all nodes.

Both arguments are required : endNode and allNodes.

The result of the compute method is :

[
{
  id: 'A',
  offset: 0,
  value: 0,
  next: { B: [Object], C: [Object], E: [Object] },
  toString: [Function]
},
{
  id: 'C',
  offset: 0,
  value: 217,
  next: { G: [Object], H: [Object] },
  toString: [Function],
  prev:
  {
  id: 'A',
  offset: 0,
  value: 0,
  next: [Object],
  toString: [Function] }
  },
{
  id: 'H',
  offset: 0,
  value: 320,
  next: { D: [Object], J: [Object] },
  toString: [Function],
  prev:
  {
  id: 'C',
  offset: 0,
  value: 217,
  next: [Object],
  toString: [Function],
  prev: [Object] }
},
{
  id: 'J',
  offset: 0,
  value: 487,
  next: {},
  toString: [Function],
  prev:
  {
  id: 'H',
  offset: 0,
  value: 320,
  next: [Object],
  toString: [Function],
  prev: [Object] }
}
]

Each element of the array is a node enhanced with some value.

  • id,offset`
  • are values given to the node when its constructor was called (new Node( {id:'', offset:0 } )).
  • next was populated by the method addNext. This object contains all next node of this node.
  • toString a function to nicely display a node
  • prev was created while computing the shortest path. It is a reference with the previous nearest node.

Calling JSON.stringify( results ) will lead to an error TypeError: Converting circular structure to JSON. To avoid this, you can remove the previous and next node for each element of the array.

results.forEach( function ( node ) {
  delete node.next;
  delete node.prev;
} );

But if you want to keep the relation between nodes, you can do has follow to keep node.id in the place of next and prev.

results.forEach( function ( node ) {
  node.next = Object.keys( node.next );
  node.prev = node.prev ? node.prev.id : undefined;
} );

Examples

Here is the graph we want to give a try from Wikipedia

var Node = require( 'shortest-way' ).Node;

var a = new Node( { id: 'A' }, true );
var b = new Node( { id: 'B' } );
var c = new Node( { id: 'C' } );
var d = new Node( { id: 'D' } );
var e = new Node( { id: 'E' } );
var f = new Node( { id: 'F' } );
var g = new Node( { id: 'G' } );
var h = new Node( { id: 'H' } );
var i = new Node( { id: 'I' } );
var j = new Node( { id: 'J' } );

a.addNext( b, 85 );
a.addNext( c, 217 );
a.addNext( e, 173 );

b.addNext( f, 80 );

c.addNext( g, 186 );
c.addNext( h, 103 );

h.addNext( d, 183 );

f.addNext( i, 250 );
i.addNext( j, 84 );

h.addNext( j, 167 );
e.addNext( j, 502 );

// call
var allNodes = [ a, b, c, d, e, f, g, h, i, j ];
var results = a.compute( j, allNodes );

var path = results.map( function ( node ) {
  return node.id
} ).join( ', ' );

console.log( path );

will output :

A, C, H, J

Transition (since version 1.1.2)

General

Untill the version 1.1.1, you could only add one weight between 2 nodes with the method addNext.

Example that don't fail:

var start = new Node( {id:'start'} );
var end = new Node( {id:'end'} );

start.addNext( end, 10 );

However, you may know another route from start node to end node. Maybe a faster route. That example that fail:

var start = new Node( {id:'start'} );
var end = new Node( {id:'end'} );

start.addNext( end, 10 );
start.addNext( end, 5 ); // Exception thrown

If you want to setup multiple routes between 2 nodes, we call it transition.

You must use the method addTransition( targetNode, weight, [options] ). This method will create a next node (T) of this node with a weight of 0 and then will setup the next node of T node to the targetNode node with a weight of weight.

The example bellow wont fail

var startNode = new Node( { id: 'start' } );		
var endNode = new Node( { id: 'end' } );		

var allNodes = [ startNode, endNode ];

allNodes.push( startNode.addTransition( endNode, 1 ) );
allNodes.push( startNode.addTransition( endNode, 2 ) );
allNodes.push( startNode.addTransition( endNode, 3 ) );

The result will be:

[ { id: 'start',
    offset: 0,
    value: 0,
    next:
     { 'transition:start>1>end': [Object],
       'transition:start>2>end': [Object],
       'transition:start>3>end': [Object] },
    toString: [Function] },
  { id: 'transition:start>1>end',
    offset: 0,
    value: 0,
    next: { end: [Object] },
    toString: [Function],
    prev:
     { id: 'start',
       offset: 0,
       value: 0,
       next: [Object],
       toString: [Function] } },
  { id: 'end',
    offset: 0,
    value: 1,
    next: {},
    toString: [Function],
    prev:
     { id: 'transition:start>1>end',
       offset: 0,
       value: 0,
       next: [Object],
       toString: [Function],
       prev: [Object] } } ]

The transition node id is build with the name 'transition:' + start_node.id + '>' + link weight + '>' + end_node.id.

Transition options

The options opional parameter is an object that can have multiple properties :

  • id the transition id
  • prefixId the transition prefix (default is 'transition'). Value ignored if id property setup.
  • startWeight the weight of the link between the startNode and the transition node.