Skip to content

Commit bae0211

Browse files
committed
feat!: keep track of nodes in a set
1 parent eaf667e commit bae0211

7 files changed

Lines changed: 32 additions & 41 deletions

File tree

src/Graph.spec-d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import { Graph } from './index.js';
77
describe('graph types', () => {
88
it('should return the given node type', () => {
99
const g = new Graph<string, { type: 'foo' | 'bar' }>();
10-
const props = g.nodes();
10+
const props = g.nodes;
1111

12-
expectTypeOf(props).toEqualTypeOf<string[]>();
12+
expectTypeOf(props).toEqualTypeOf<Set<string>>();
1313
});
1414

1515
it('should return the given edge properties type', () => {

src/Graph.spec.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describe('Graph', function () {
1111
graph.addNode('a');
1212
graph.addNode('b');
1313

14-
const nodes = graph.nodes();
14+
const nodes = graph.nodes;
1515

1616
expect(nodes).toHaveLength(2);
1717
expect(nodes).toContain('a');
@@ -20,7 +20,7 @@ describe('Graph', function () {
2020

2121
it('Should chain addNode.', function () {
2222
const graph = new Graph().addNode('a').addNode('b');
23-
const nodes = graph.nodes();
23+
const nodes = graph.nodes;
2424

2525
expect(nodes).toHaveLength(2);
2626
expect(nodes).toContain('a');
@@ -34,14 +34,14 @@ describe('Graph', function () {
3434
graph.removeNode('a');
3535
graph.removeNode('b');
3636

37-
const nodes = graph.nodes();
37+
const nodes = graph.nodes;
3838
expect(nodes).toHaveLength(0);
3939
});
4040

4141
it('Should chain removeNode.', function () {
4242
const graph = new Graph().addNode('a').addNode('b').removeNode('a').removeNode('b');
4343

44-
const nodes = graph.nodes();
44+
const nodes = graph.nodes;
4545
expect(nodes).toHaveLength(0);
4646
});
4747

@@ -64,7 +64,7 @@ describe('Graph', function () {
6464
expect(adjacentNodes).toHaveLength(1);
6565
expect(adjacentNodes?.has('b')).toBe(true);
6666

67-
const nodes = graph.nodes();
67+
const nodes = graph.nodes;
6868
expect(nodes).toHaveLength(2);
6969
expect(nodes).toContain('a');
7070
expect(nodes).toContain('b');
@@ -99,7 +99,7 @@ describe('Graph', function () {
9999
graph.addEdge('a', 'b');
100100
graph.removeEdge('a', 'b');
101101

102-
const nodes = graph.nodes();
102+
const nodes = graph.nodes;
103103
expect(nodes).toHaveLength(2);
104104
expect(nodes).toContain('a');
105105
expect(nodes).toContain('b');
@@ -123,7 +123,7 @@ describe('Graph', function () {
123123
it('Should return undefined for unknown nodes.', function () {
124124
const graph = new Graph();
125125
expect(graph.adjacent('a')).toEqual(undefined);
126-
expect(graph.nodes()).toHaveLength(0);
126+
expect(graph.nodes).toHaveLength(0);
127127
});
128128

129129
it('Should do nothing if removing an edge that does not exist.', function () {

src/Graph.ts

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ import { EdgeWeight, SerializedInput } from './types.js';
33
import { deserializeGraph } from './utils/deserializeGraph.js';
44

55
export class Graph<Node = string, LinkProps = never> {
6+
/**
7+
* Contains all the nodes added to the graph.
8+
*/
9+
nodes: Set<Node> = new Set();
10+
611
/**
712
* The adjacency list of the graph.
813
*/
@@ -33,6 +38,10 @@ export class Graph<Node = string, LinkProps = never> {
3338
* If node was not already added, this function sets up an empty adjacency list.
3439
*/
3540
addNode(node: Node): this {
41+
if (!this.nodes.has(node)) {
42+
this.nodes.add(node);
43+
}
44+
3645
if (!this.edges.has(node)) {
3746
this.edges.set(node, new Set());
3847
}
@@ -47,6 +56,7 @@ export class Graph<Node = string, LinkProps = never> {
4756
removeNode(node: Node): this {
4857
// Remove outgoing edges (and signal that the node no longer exists).
4958
this.edges.delete(node);
59+
this.nodes.delete(node);
5060

5161
// Remove ingoing edges
5262
for (const adjacentNodes of this.edges.values()) {
@@ -56,28 +66,6 @@ export class Graph<Node = string, LinkProps = never> {
5666
return this;
5767
}
5868

59-
/**
60-
* Gets the list of nodes that have been added to the graph.
61-
*/
62-
nodes(): Node[] {
63-
const nodes: Set<Node> = new Set();
64-
65-
for (const edgeKey of this.edges.keys()) {
66-
nodes.add(edgeKey);
67-
68-
const adjacentNodes = this.edges.get(edgeKey)?.values();
69-
if (!adjacentNodes) {
70-
throw new Error(`Missing adjacent set for node ${edgeKey}`);
71-
}
72-
73-
for (const adjacentNode of adjacentNodes) {
74-
nodes.add(adjacentNode);
75-
}
76-
}
77-
78-
return Array.from(nodes);
79-
}
80-
8169
/**
8270
* Gets the adjacent node set for the given node.
8371
*/

src/algorithms/depthFirstSearch/depthFirstSearch.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export function depthFirstSearch<Node, LinkProps>(
1212
graph: Graph<Node, LinkProps>,
1313
opts: DepthFirstSearchOptions<NoInfer<Node>, NoInfer<LinkProps>> = {},
1414
): Node[] {
15-
const { sourceNodes = graph.nodes(), includeSourceNodes = true } = opts;
15+
const { sourceNodes = Array.from(graph.nodes), includeSourceNodes = true } = opts;
1616

1717
const visited: Set<Node> = new Set();
1818
const visiting: Set<Node> = new Set();

src/algorithms/shortestPath/dijkstra.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export function dijkstra<Node>(
1010
source: NoInfer<Node>,
1111
destination: NoInfer<Node>,
1212
) {
13-
const nodes = graph.nodes();
13+
const nodes = graph.nodes;
1414
const { d, p, q } = tracks;
1515

1616
initializeSingleSource(nodes, tracks, source, destination);
@@ -28,14 +28,14 @@ export function dijkstra<Node>(
2828
}
2929

3030
function initializeSingleSource<Node>(
31-
nodes: Node[],
31+
nodes: Set<Node>,
3232
{ d }: TraversingTracks<NoInfer<Node>>,
3333
source: NoInfer<Node>,
3434
destination: NoInfer<Node>,
3535
) {
36-
for (const node of nodes) {
36+
nodes.forEach((node) => {
3737
d.set(node, Infinity);
38-
}
38+
});
3939

4040
if (d.get(source) !== Infinity) {
4141
throw new Error('Source node is not in the graph');
@@ -49,10 +49,10 @@ function initializeSingleSource<Node>(
4949
}
5050

5151
function initializePriorityQueue<Node>(
52-
nodes: Node[],
52+
nodes: Set<Node>,
5353
{ q }: TraversingTracks<NoInfer<Node>>,
5454
) {
55-
for (let i = 0; i < nodes.length; i++) {
56-
q.add(nodes[i]);
57-
}
55+
nodes.forEach((node) => {
56+
q.add(node);
57+
});
5858
}

src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ export { cloneGraph } from './utils/cloneGraph.js';
1616
export { hasCycle } from './utils/hasCycle.js';
1717
export { serializeGraph } from './utils/serializeGraph.js';
1818
export { deserializeGraph } from './utils/deserializeGraph.js';
19+
export { findNodes } from './utils/findNodes.js';
20+
export { getNode } from './utils/getNode.js';
21+
export { getFirstNode } from './utils/getFirstNode.js';

src/utils/serializeGraph.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function serializeGraph<Node, LinkProps, IncludeDefaultWeight extends boo
2727
const { includeDefaultWeight = false } = opts;
2828

2929
const serialized: Serialized<Node, LinkProps> = {
30-
nodes: graph.nodes(),
30+
nodes: Array.from(graph.nodes),
3131
links: [],
3232
};
3333

0 commit comments

Comments
 (0)