Skip to content

Instantly share code, notes, and snippets.

@BenDMyers
Created January 3, 2022 06:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BenDMyers/4d9b2b7b1fef4e20a4325d84f82bb1cf to your computer and use it in GitHub Desktop.
Save BenDMyers/4d9b2b7b1fef4e20a4325d84f82bb1cf to your computer and use it in GitHub Desktop.
RWC: Path Between Points
/**
* @typedef {object} Point
* @property {string} name
* @property {string[]} connections
*/
/** @type {Point[]} */
const listOfPoints = [
{ name: 'A', connections: ['B', 'C'] },
{ name: 'B', connections: ['A', 'E'] },
{ name: 'C', connections: ['A', 'D'] },
{ name: 'D', connections: ['C'] },
{ name: 'E', connections: ['B', 'F'] },
{ name: 'F', connections: ['E'] },
]
/**
* Comparator/sort function for sorting two arrays by their length in descending order.
* @param {string[]} a
* @param {string[]} b
* @returns {number} relative order between the two arrays
*/
function byLength(a, b) {
return b.length - a.length;
}
/**
* Recursively determines the shortest path from the current point to the desired end point.
* If no paths between the two points exist, aborts with `false`.
* Does not revisit visited points (doing so wouldn't create the shortest path, and would risk cycles)
* @param {Object<string, string[]>} normalizedPoints
* @param {string} currentPoint
* @param {string} end
* @param {string[]} visited
* @returns {string[] | false}
*/
function getShortestPathToPoint(normalizedPoints, currentPoint, end, visited) {
// Base case: you've reached the end
if (currentPoint === end) {
return [end];
}
// Get possible connections
const connections = normalizedPoints[currentPoint].filter(point => !visited.includes(point));
// No more possible connections? Abort.
if (connections.length === 0) {
return false;
}
const viablePaths = connections
.map(nextPoint => getShortestPathToPoint(normalizedPoints, nextPoint, end, [...visited, currentPoint]))
.filter(path => (path !== false));
// None of the connections were fruitful? Abort.
if (viablePaths.length === 0) {
return false;
}
// At least one connection proved successful! Return the shortest one.
const [shortestPath] = viablePaths.sort(byLength);
return [currentPoint, ...shortestPath];
}
/**
* Prints the shortest path between two points, or "no path" if none exist
* @param {Point[]} points
* @param {string} start
* @param {string} end
*/
function pathBetweenPoints(points, start, end) {
const normalizedPoints = points.reduce((normalized, point) => {
return {...normalized, [point.name]: point.connections};
}, {});
const shortestPath = getShortestPathToPoint(normalizedPoints, start, end, []);
shortestPath ?
console.log(shortestPath.join(' -> ')) :
console.log('no path');
}
pathBetweenPoints(listOfPoints, 'A', 'F');
pathBetweenPoints(listOfPoints, 'D', 'B');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment