Created
January 18, 2023 14:49
-
-
Save ncalm/f1b9beb84f3268cbc6ce707f06bc4f44 to your computer and use it in GitHub Desktop.
This gist supports breadth-first search of graph data using Excel Lambda functions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Note, this function is used by the functions in the graph namespace | |
*/ | |
IFOMITTED = LAMBDA(arg, then, [else], | |
LET(_else, IF(ISOMITTED(else), arg, else), IF(ISOMITTED(arg), then, _else)) | |
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Note: import these functions to a new namespace in a workbook. The namespace should be called "graph" | |
*/ | |
author = "Owen Price"; | |
/* | |
An array of graph data (edit as necessary) | |
First column is the node as a single value | |
Second column is the neighbors of that node, as a ", "-separated string | |
e.g. this row tells us that node A has neighbors B and C | |
{"A", "B, C"} | |
*/ | |
data = Sheet1!A2:B4; | |
/* | |
Returns the neighbors of a node from an array of graph data | |
*/ | |
get_neighbors_fn = LAMBDA(data, | |
LAMBDA(node, | |
LET( | |
_neighbors, INDEX(FILTER(TAKE(data, , -1), TAKE(data, , 1) = node), 1, 1), | |
TEXTSPLIT(_neighbors, , ", ") | |
) | |
) | |
); | |
/* | |
Returns a function that returns the neighbors of a node as a vertical array from the array | |
of data defined in the named range graph.data | |
*/ | |
get_neighbors = graph.get_neighbors_fn(graph.data); | |
/* | |
Returns an array detailing the nodes visited during a breadth-first search of graph data | |
*/ | |
breadth_first_search = LAMBDA(queue, end, [visited], [iteration], | |
LET( | |
//dequeue | |
_iteration, IFOMITTED(iteration, 1, iteration + 1), | |
_node, INDEX(TAKE(queue, 1), 1, 1), | |
_visited, IFOMITTED(visited, HSTACK(_node, "None")), | |
_is_undiscovered, LAMBDA(node, ISERROR(XMATCH(node, TAKE(_visited, , 1)))), | |
_neighbors, graph.get_neighbors(_node), | |
_end_is_neighbor, NOT(ISERROR(XMATCH(end, _neighbors))), | |
_newqueue, | |
IF( | |
OR(ISERROR(DROP(queue, 1))), | |
_neighbors, | |
REDUCE( | |
DROP(queue, 1), | |
_neighbors, | |
LAMBDA(a, b, IF(_is_undiscovered(b), VSTACK(a, b), a)) | |
) | |
), | |
_newvisited, REDUCE( | |
_visited, | |
_neighbors, | |
LAMBDA(a, b, IF(_is_undiscovered(b), VSTACK(a, HSTACK(b, _node)), a)) | |
), | |
_result, IF( | |
OR(_node = end, _end_is_neighbor), | |
VSTACK(_visited, HSTACK(end, _node)), | |
graph.breadth_first_search(_newqueue, end, _newvisited, _iteration) | |
), | |
_result | |
) | |
); | |
/* | |
Returns an array tracing the path between start and end using | |
a search algorithm function passed to search_function (e.g. breadth_first_search). | |
The search_function must return a "visited array" similar in structure to that | |
returned by breadth_first_search. | |
*/ | |
get_path = LAMBDA(start, next, search_function, [visited], [path], | |
LET( | |
_visited, IFOMITTED(visited, search_function(start, next)), | |
_step, FILTER(_visited, TAKE(_visited, , 1) = next), | |
_path, IFOMITTED(path, _step, VSTACK(_step, path)), | |
_next, INDEX(_step, 1, 2), | |
_result, IF( | |
_next = start, | |
CHOOSECOLS(_path, {2, 1}), | |
graph.get_path(start, _next, search_function, _visited, _path) | |
), | |
_result | |
) | |
); | |
/* | |
Returns a single-value representing a path returned by get_path | |
*/ | |
get_path_text = LAMBDA(start, end, search_function, | |
LET( | |
_path, graph.get_path(start, end, search_function), | |
TEXTJOIN(" " & UNICHAR(10132) & " ", TRUE, UNIQUE(TOCOL(_path))) | |
) | |
); | |
/* | |
Returns an integer represenging the count of edges traversed by a search | |
*/ | |
get_distance = | |
LAMBDA(from, to, search_function, | |
LET( | |
_path, graph.get_path(from, to, search_function), | |
ROWS(_path) | |
) | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment