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

dag-progress

v2.0.1

Published

Calculate user progress for each vertex in an acyclic digraph

Downloads

19

Readme

Progress in a Directed Acyclic Graph

Build Status

Given a Directed Acyclic Graph (or Acyclic Digraph for short), calculate a Progress value for each vertex in the graph.

I'm using this to display meaningful progress values to a User when they are going through a branching interaction which may allow them to skip over parts or take different courses through that interaction. Representing the interaction as a directed acyclic graph allows this easily.

The progress calculation is very simple: Given a vertex, the length of the longest path possible from any source to that vertex divided by the length of the longest path possible from any source to any sink which conatains that vertex is the progress value for that vertex. This definition works surprisingly well given even quite disparate branch lengths in an interaction, and handles no-progress vertices quite well, too.

Note: As you might expect from a directed acyclic graph, this library does not support cycles in your story graph. To get a meaningful progress value in such cases, you should break any cycles before handing the graph to this code.

Dependencies

  • toposort is used for the one sort operation that occurs.

API

Preface: Types

  • AdjacencyListMap = { [nodeId: string]: Array<string> } Object which maps a Node Id to a list of Next Node Ids.
  • NodeOptionsMap = { [nodeId: string]: NodeOptions } Maps a Node Id to options for that Node.
    • NodeOptions = { ... } Options for a given Node. All props here are optional.
      • weight: number The weight of this Node, or how much it contributes to the total progress.
        • Default value: 1
  • ProgressMap = { [nodeId: string]: Progress } Maps a Node Id to Progress values for that Node.
    • Progress = { ... } Progress values for a given Node.
      • value: number The progress value, from 0.0 to 1.0, of this Node.
      • own: number The progress contribution this node itself makes to the longest/heaviest path which contains it.
      • before: number The largest progress before this node of all paths that contain this node.
      • remaining: number The remaining progress after this node.
      • rawValue: number The raw weight value, from 0.0 to pathTotal, of this Node.
      • rawOwn: number The raw weight contribution this node itself makes to the longest/heaviest path which contains it.
        • This will equal the weight option passed for this node, or the default 1 if no weight was specified.
      • rawBefore: number The largest weight before this node out of all paths that contain this node.
      • rawRemaining: number The remaining weight after this node.
      • pathTotal: number The total weight of the heaviest path which contains this node.

dagProgress( adjacencyList: AdjacencyListMap, nodeOptions?: NodeOptionsMap ) => ProgressMap

Calculates the Progress value of each node in the DAG, as represented by its adjacency list.

  • adjacencyList: AdjacencyListMap The adjacency map for this DAG.
  • nodeOptions: NodeOptionsMap The options for each Node, if they have any. Passing nothing gives all nodes the default options.

Example

Note: Example is in ES6.

// During interaction initialization...

import dagProgress from 'dag-progress';
let storyDag = myThingThatMakesMyStoryIntoAnAdjacencyList( myStory );
let vertexProgresses = dagProgress( storyDag );

// Then, in some display component...

const render = ({ props }) => {
	let stepProgress = vertexProgresses[ props.page.id ];

	return (
		h( 'div', { 'class': 'interaction-progress' }, [
			`Progress: ${ Math.round( stepProgress.value * 100 ) }%`
		])
	);
};

Degenerate Cases

Single Vertex Paths

A single Vertex will, if does not have weight: 0, have the Progress value 1. If it does have weight: 0 then it's a NaN which makes you a bad person who should feel bad.