Skip to content

Instantly share code, notes, and snippets.

@JonathanTurnock
Last active August 30, 2021 19:30
Show Gist options
  • Save JonathanTurnock/25d11ac038aea2aeac22e1a64f3b20c1 to your computer and use it in GitHub Desktop.
Save JonathanTurnock/25d11ac038aea2aeac22e1a64f3b20c1 to your computer and use it in GitHub Desktop.
JS implementation Dijkstra's Algorithm
import { Graph } from "./graph";
describe("Graph", () => {
let graph: Graph;
beforeAll(() => {
graph = new Graph();
graph.addNode("A");
graph.addNode("B");
graph.addNode("C");
graph.addNode("D");
graph.addNode("E");
graph.addNode("F");
graph.addEdge(["A", "B"], 3);
graph.addEdge(["A", "C"], 5);
graph.addEdge(["B", "D"], 3);
graph.addEdge(["B", "C"], 3);
graph.addEdge(["C", "D"], 3);
graph.addEdge(["C", "E"], 6);
graph.addEdge(["E", "F"], 1);
graph.addEdge(["D", "F"], 10);
});
it("should return most efficient path from A -> B", () => {
expect(graph.findShortest("A", "B")).toEqual(["A", "B"]);
});
it("should return most efficient path from A -> D", () => {
expect(graph.findShortest("A", "D")).toEqual(["A", "B", "D"]);
});
it("should return most efficient path from A -> F", () => {
expect(graph.findShortest("A", "F")).toEqual(["A", "C", "E", "F"]);
});
});
import { find, minBy, pull, reverse } from "lodash";
import { Node, Edge } from "./types";
export class Graph {
private nodes: Node[] = [];
private edges: Edge[] = [];
addNode(id: string) {
this.nodes.push({ id });
}
addEdge(nodes: string[], cost: number) {
this.edges.push({ nodes, cost });
}
/**
* Calculates the shortest path based on cost from source to destination
* @param source
* @param destination
*/
findShortest(source: string, destination: string) {
const visited = [];
const unvisited = this.nodes.map(({ id }) => ({
node: id,
cost: Infinity,
previous: undefined,
}));
find(unvisited, { node: source }).cost = 0;
while (unvisited.length > 0) {
const currentNode = minBy(unvisited, "cost");
const costAdjustedNeighbours = this.getNeighbours(currentNode.node)?.map(
({ node, cost }) => ({
node,
cost: cost + currentNode.cost,
previous: currentNode.node,
}),
);
costAdjustedNeighbours.forEach(({ node, cost }) => {
const neighbour = find(unvisited, { node });
if (neighbour && cost < neighbour.cost) {
neighbour.cost = cost;
neighbour.previous = currentNode.node;
}
});
visited.push(currentNode);
pull(unvisited, currentNode);
}
const map = [];
let current = find(visited, { node: destination });
while (!map.includes(source)) {
map.push(current.node);
current = find(visited, { node: current.previous });
}
return reverse(map);
}
private getNeighbours(node: string) {
return this.edges
.filter(({ nodes }) => nodes?.includes(node))
.map(({ nodes, cost }) => ({
node: nodes.find((it) => it !== node),
cost,
}));
}
}
export type Node = { id: string };
export type Edge = {
nodes: string[];
cost: number;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment