@gabrielrufino/cube 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/.github/workflows/cd.yml +26 -0
  2. package/.github/workflows/ci.yml +23 -0
  3. package/LICENSE +24 -0
  4. package/README.md +1695 -0
  5. package/build/Algorithms/index.d.ts +3 -0
  6. package/build/Algorithms/index.js +24 -0
  7. package/build/Array/IArray.d.ts +9 -0
  8. package/build/Array/IArray.js +2 -0
  9. package/build/Array/index.d.ts +12 -0
  10. package/build/Array/index.js +79 -0
  11. package/build/BinarySearchTree/BinarySearchTreeNode.d.ts +7 -0
  12. package/build/BinarySearchTree/BinarySearchTreeNode.js +22 -0
  13. package/build/BinarySearchTree/IBinarySearchNodeOptions.d.ts +5 -0
  14. package/build/BinarySearchTree/IBinarySearchNodeOptions.js +2 -0
  15. package/build/BinarySearchTree/IBinarySearchTree.d.ts +14 -0
  16. package/build/BinarySearchTree/IBinarySearchTree.js +2 -0
  17. package/build/BinarySearchTree/IBinarySearchTreeData.d.ts +7 -0
  18. package/build/BinarySearchTree/IBinarySearchTreeData.js +2 -0
  19. package/build/BinarySearchTree/index.d.ts +24 -0
  20. package/build/BinarySearchTree/index.js +199 -0
  21. package/build/DataStructure/index.d.ts +6 -0
  22. package/build/DataStructure/index.js +32 -0
  23. package/build/Deck/IDeck.d.ts +9 -0
  24. package/build/Deck/IDeck.js +2 -0
  25. package/build/Deck/index.d.ts +12 -0
  26. package/build/Deck/index.js +76 -0
  27. package/build/Dictionary/IDictionary.d.ts +16 -0
  28. package/build/Dictionary/IDictionary.js +2 -0
  29. package/build/Dictionary/IDictionaryData.d.ts +4 -0
  30. package/build/Dictionary/IDictionaryData.js +2 -0
  31. package/build/Dictionary/index.d.ts +21 -0
  32. package/build/Dictionary/index.js +110 -0
  33. package/build/DoublyLinkedList/IDoublyLinkedList.d.ts +16 -0
  34. package/build/DoublyLinkedList/IDoublyLinkedList.js +2 -0
  35. package/build/DoublyLinkedList/Node.d.ts +6 -0
  36. package/build/DoublyLinkedList/Node.js +11 -0
  37. package/build/DoublyLinkedList/index.d.ts +32 -0
  38. package/build/DoublyLinkedList/index.js +221 -0
  39. package/build/Graph/GraphNodeNotFoundError.d.ts +3 -0
  40. package/build/Graph/GraphNodeNotFoundError.js +27 -0
  41. package/build/Graph/GraphSearchNodeStates.d.ts +6 -0
  42. package/build/Graph/GraphSearchNodeStates.js +10 -0
  43. package/build/Graph/IGraph.d.ts +16 -0
  44. package/build/Graph/IGraph.js +2 -0
  45. package/build/Graph/IGraphOptions.d.ts +7 -0
  46. package/build/Graph/IGraphOptions.js +2 -0
  47. package/build/Graph/index.d.ts +20 -0
  48. package/build/Graph/index.js +187 -0
  49. package/build/HashTable/IHashTable.d.ts +9 -0
  50. package/build/HashTable/IHashTable.js +2 -0
  51. package/build/HashTable/IHashTableData.d.ts +4 -0
  52. package/build/HashTable/IHashTableData.js +2 -0
  53. package/build/HashTable/IHashTableInputs.d.ts +4 -0
  54. package/build/HashTable/IHashTableInputs.js +2 -0
  55. package/build/HashTable/IHashTableOptions.d.ts +4 -0
  56. package/build/HashTable/IHashTableOptions.js +2 -0
  57. package/build/HashTable/index.d.ts +16 -0
  58. package/build/HashTable/index.js +83 -0
  59. package/build/HashTableLinearProbing/HashTableLinearProbingElement.d.ts +6 -0
  60. package/build/HashTableLinearProbing/HashTableLinearProbingElement.js +19 -0
  61. package/build/HashTableLinearProbing/IHashTableLinearProbing.d.ts +9 -0
  62. package/build/HashTableLinearProbing/IHashTableLinearProbing.js +2 -0
  63. package/build/HashTableLinearProbing/IHashTableLinearProbingData.d.ts +5 -0
  64. package/build/HashTableLinearProbing/IHashTableLinearProbingData.js +2 -0
  65. package/build/HashTableLinearProbing/IHashTableLinearProbingInputs.d.ts +4 -0
  66. package/build/HashTableLinearProbing/IHashTableLinearProbingInputs.js +2 -0
  67. package/build/HashTableLinearProbing/IHashTableLinearProbingOptions.d.ts +4 -0
  68. package/build/HashTableLinearProbing/IHashTableLinearProbingOptions.js +2 -0
  69. package/build/HashTableLinearProbing/index.d.ts +20 -0
  70. package/build/HashTableLinearProbing/index.js +120 -0
  71. package/build/HashTableSeparateChaining/HashTableSeparateChainingElement.d.ts +6 -0
  72. package/build/HashTableSeparateChaining/HashTableSeparateChainingElement.js +21 -0
  73. package/build/HashTableSeparateChaining/IHashTableSeparateChaining.d.ts +10 -0
  74. package/build/HashTableSeparateChaining/IHashTableSeparateChaining.js +2 -0
  75. package/build/HashTableSeparateChaining/IHashTableSeparateChainingData.d.ts +5 -0
  76. package/build/HashTableSeparateChaining/IHashTableSeparateChainingData.js +2 -0
  77. package/build/HashTableSeparateChaining/IHashTableSeparateChainingInputs.d.ts +4 -0
  78. package/build/HashTableSeparateChaining/IHashTableSeparateChainingInputs.js +2 -0
  79. package/build/HashTableSeparateChaining/IHashTableSeparateChainingOptions.d.ts +4 -0
  80. package/build/HashTableSeparateChaining/IHashTableSeparateChainingOptions.js +2 -0
  81. package/build/HashTableSeparateChaining/index.d.ts +19 -0
  82. package/build/HashTableSeparateChaining/index.js +104 -0
  83. package/build/LinkedList/ILinkedList.d.ts +13 -0
  84. package/build/LinkedList/ILinkedList.js +2 -0
  85. package/build/LinkedList/ILinkedListItem.d.ts +5 -0
  86. package/build/LinkedList/ILinkedListItem.js +2 -0
  87. package/build/LinkedList/Node.d.ts +5 -0
  88. package/build/LinkedList/Node.js +10 -0
  89. package/build/LinkedList/index.d.ts +24 -0
  90. package/build/LinkedList/index.js +169 -0
  91. package/build/List/index.d.ts +1 -0
  92. package/build/List/index.js +8 -0
  93. package/build/MaxHeap/IMaxHeap.d.ts +9 -0
  94. package/build/MaxHeap/IMaxHeap.js +2 -0
  95. package/build/MaxHeap/IMaxHeapOptions.d.ts +5 -0
  96. package/build/MaxHeap/IMaxHeapOptions.js +2 -0
  97. package/build/MaxHeap/index.d.ts +19 -0
  98. package/build/MaxHeap/index.js +102 -0
  99. package/build/MinHeap/IMinHeap.d.ts +9 -0
  100. package/build/MinHeap/IMinHeap.js +2 -0
  101. package/build/MinHeap/IMinHeapOptions.d.ts +5 -0
  102. package/build/MinHeap/IMinHeapOptions.js +2 -0
  103. package/build/MinHeap/index.d.ts +19 -0
  104. package/build/MinHeap/index.js +102 -0
  105. package/build/Queue/IQueue.d.ts +8 -0
  106. package/build/Queue/IQueue.js +2 -0
  107. package/build/Queue/index.d.ts +11 -0
  108. package/build/Queue/index.js +74 -0
  109. package/build/Set/ISet.d.ts +15 -0
  110. package/build/Set/ISet.js +2 -0
  111. package/build/Set/index.d.ts +21 -0
  112. package/build/Set/index.js +132 -0
  113. package/build/Stack/IStack.d.ts +8 -0
  114. package/build/Stack/IStack.js +2 -0
  115. package/build/Stack/index.d.ts +11 -0
  116. package/build/Stack/index.js +64 -0
  117. package/build/index.d.ts +17 -0
  118. package/build/index.js +40 -0
  119. package/coverage/clover.xml +706 -0
  120. package/coverage/coverage-final.json +19 -0
  121. package/coverage/lcov-report/base.css +224 -0
  122. package/coverage/lcov-report/block-navigation.js +87 -0
  123. package/coverage/lcov-report/favicon.png +0 -0
  124. package/coverage/lcov-report/index.html +327 -0
  125. package/coverage/lcov-report/prettify.css +1 -0
  126. package/coverage/lcov-report/prettify.js +2 -0
  127. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  128. package/coverage/lcov-report/sorter.js +196 -0
  129. package/coverage/lcov-report/src/Algorithms/index.html +117 -0
  130. package/coverage/lcov-report/src/Algorithms/index.ts.html +149 -0
  131. package/coverage/lcov-report/src/Array/index.html +117 -0
  132. package/coverage/lcov-report/src/Array/index.ts.html +290 -0
  133. package/coverage/lcov-report/src/DataStructure/index.html +117 -0
  134. package/coverage/lcov-report/src/DataStructure/index.ts.html +131 -0
  135. package/coverage/lcov-report/src/Deck/index.html +117 -0
  136. package/coverage/lcov-report/src/Deck/index.ts.html +239 -0
  137. package/coverage/lcov-report/src/Dictionary/index.html +117 -0
  138. package/coverage/lcov-report/src/Dictionary/index.ts.html +350 -0
  139. package/coverage/lcov-report/src/DoublyLinkedList/Node.ts.html +113 -0
  140. package/coverage/lcov-report/src/DoublyLinkedList/index.html +132 -0
  141. package/coverage/lcov-report/src/DoublyLinkedList/index.ts.html +836 -0
  142. package/coverage/lcov-report/src/HashTable/index.html +117 -0
  143. package/coverage/lcov-report/src/HashTable/index.ts.html +320 -0
  144. package/coverage/lcov-report/src/HashTableLinearProbing/index.html +117 -0
  145. package/coverage/lcov-report/src/HashTableLinearProbing/index.ts.html +95 -0
  146. package/coverage/lcov-report/src/HashTableSeparateChaining/HashTableSeparateChainingElement.ts.html +149 -0
  147. package/coverage/lcov-report/src/HashTableSeparateChaining/index.html +132 -0
  148. package/coverage/lcov-report/src/HashTableSeparateChaining/index.ts.html +398 -0
  149. package/coverage/lcov-report/src/LinkedList/Node.ts.html +110 -0
  150. package/coverage/lcov-report/src/LinkedList/index.html +132 -0
  151. package/coverage/lcov-report/src/LinkedList/index.ts.html +641 -0
  152. package/coverage/lcov-report/src/List/index.html +117 -0
  153. package/coverage/lcov-report/src/List/index.ts.html +89 -0
  154. package/coverage/lcov-report/src/Queue/index.html +117 -0
  155. package/coverage/lcov-report/src/Queue/index.ts.html +218 -0
  156. package/coverage/lcov-report/src/Set/index.html +117 -0
  157. package/coverage/lcov-report/src/Set/index.ts.html +488 -0
  158. package/coverage/lcov-report/src/Stack/index.html +117 -0
  159. package/coverage/lcov-report/src/Stack/index.ts.html +215 -0
  160. package/coverage/lcov-report/src/index.html +117 -0
  161. package/coverage/lcov-report/src/index.ts.html +125 -0
  162. package/coverage/lcov.info +1346 -0
  163. package/package.json +42 -0
  164. package/src/Algorithms/index.spec.ts +15 -0
  165. package/src/Algorithms/index.ts +21 -0
  166. package/src/Array/IArray.ts +10 -0
  167. package/src/Array/index.spec.ts +151 -0
  168. package/src/Array/index.ts +68 -0
  169. package/src/BinarySearchTree/BinarySearchTreeNode.spec.ts +29 -0
  170. package/src/BinarySearchTree/BinarySearchTreeNode.ts +23 -0
  171. package/src/BinarySearchTree/IBinarySearchNodeOptions.ts +6 -0
  172. package/src/BinarySearchTree/IBinarySearchTree.ts +16 -0
  173. package/src/BinarySearchTree/IBinarySearchTreeData.ts +9 -0
  174. package/src/BinarySearchTree/index.spec.ts +486 -0
  175. package/src/BinarySearchTree/index.ts +198 -0
  176. package/src/DataStructure/index.spec.ts +25 -0
  177. package/src/DataStructure/index.ts +15 -0
  178. package/src/Deck/IDeck.ts +10 -0
  179. package/src/Deck/index.spec.ts +131 -0
  180. package/src/Deck/index.ts +51 -0
  181. package/src/Dictionary/IDictionary.ts +18 -0
  182. package/src/Dictionary/IDictionaryData.ts +5 -0
  183. package/src/Dictionary/index.spec.ts +310 -0
  184. package/src/Dictionary/index.ts +88 -0
  185. package/src/DoublyLinkedList/IDoublyLinkedList.ts +18 -0
  186. package/src/DoublyLinkedList/Node.ts +9 -0
  187. package/src/DoublyLinkedList/index.spec.ts +478 -0
  188. package/src/DoublyLinkedList/index.ts +250 -0
  189. package/src/Graph/GraphNodeNotFoundError.ts +7 -0
  190. package/src/Graph/GraphSearchNodeStates.ts +8 -0
  191. package/src/Graph/IGraph.ts +15 -0
  192. package/src/Graph/IGraphOptions.ts +6 -0
  193. package/src/Graph/index.spec.ts +318 -0
  194. package/src/Graph/index.ts +170 -0
  195. package/src/HashTable/IHashTable.ts +11 -0
  196. package/src/HashTable/IHashTableData.ts +5 -0
  197. package/src/HashTable/IHashTableInputs.ts +5 -0
  198. package/src/HashTable/IHashTableOptions.ts +5 -0
  199. package/src/HashTable/index.spec.ts +136 -0
  200. package/src/HashTable/index.ts +78 -0
  201. package/src/HashTableLinearProbing/HashTableLinearProbingElement.spec.ts +28 -0
  202. package/src/HashTableLinearProbing/HashTableLinearProbingElement.ts +18 -0
  203. package/src/HashTableLinearProbing/IHashTableLinearProbing.ts +11 -0
  204. package/src/HashTableLinearProbing/IHashTableLinearProbingData.ts +7 -0
  205. package/src/HashTableLinearProbing/IHashTableLinearProbingInputs.ts +5 -0
  206. package/src/HashTableLinearProbing/IHashTableLinearProbingOptions.ts +5 -0
  207. package/src/HashTableLinearProbing/index.spec.ts +266 -0
  208. package/src/HashTableLinearProbing/index.ts +128 -0
  209. package/src/HashTableSeparateChaining/HashTableSeparateChainingElement.ts +21 -0
  210. package/src/HashTableSeparateChaining/IHashTableSeparateChaining.ts +12 -0
  211. package/src/HashTableSeparateChaining/IHashTableSeparateChainingData.ts +7 -0
  212. package/src/HashTableSeparateChaining/IHashTableSeparateChainingInputs.ts +5 -0
  213. package/src/HashTableSeparateChaining/IHashTableSeparateChainingOptions.ts +5 -0
  214. package/src/HashTableSeparateChaining/index.spec.ts +173 -0
  215. package/src/HashTableSeparateChaining/index.ts +104 -0
  216. package/src/LinkedList/ILinkedList.ts +15 -0
  217. package/src/LinkedList/ILinkedListItem.ts +6 -0
  218. package/src/LinkedList/Node.ts +8 -0
  219. package/src/LinkedList/index.spec.ts +355 -0
  220. package/src/LinkedList/index.ts +185 -0
  221. package/src/List/index.ts +1 -0
  222. package/src/MaxHeap/IMaxHeap.ts +10 -0
  223. package/src/MaxHeap/IMaxHeapOptions.ts +6 -0
  224. package/src/MaxHeap/index.spec.ts +161 -0
  225. package/src/MaxHeap/index.ts +91 -0
  226. package/src/MinHeap/IMinHeap.ts +10 -0
  227. package/src/MinHeap/IMinHeapOptions.ts +6 -0
  228. package/src/MinHeap/index.spec.ts +161 -0
  229. package/src/MinHeap/index.ts +91 -0
  230. package/src/Queue/IQueue.ts +9 -0
  231. package/src/Queue/index.spec.ts +92 -0
  232. package/src/Queue/index.ts +44 -0
  233. package/src/Set/ISet.ts +17 -0
  234. package/src/Set/index.spec.ts +246 -0
  235. package/src/Set/index.ts +134 -0
  236. package/src/Stack/IStack.ts +9 -0
  237. package/src/Stack/index.spec.ts +108 -0
  238. package/src/Stack/index.ts +43 -0
  239. package/src/index.ts +17 -0
  240. package/tsconfig.json +104 -0
@@ -0,0 +1,8 @@
1
+ /* eslint-disable no-unused-vars */
2
+ enum GraphSearchNodeStates {
3
+ UNEXPLORED,
4
+ DISCOVERED,
5
+ EXPLORED,
6
+ }
7
+
8
+ export default GraphSearchNodeStates;
@@ -0,0 +1,15 @@
1
+ interface IGraph {
2
+ get isDirected(): boolean;
3
+ get data(): { [key: string]: string[] };
4
+ get size(): number;
5
+ get nodes(): string[];
6
+ get edges(): [string, string][];
7
+ insert(_node: string): string | null;
8
+ // Remove(_node: string): string | null;
9
+ connect(_node1: string, _node2: string): [string, string];
10
+ // Disconnect(_node1: string, _node2: string): [string, string] | null;
11
+ breadthFirstSearch(_startNode: string, _callback: (_node: string) => void): void;
12
+ getDistancesFrom(_node: string): { [key: string]: number };
13
+ }
14
+
15
+ export default IGraph;
@@ -0,0 +1,6 @@
1
+ interface IGraphOptions {
2
+ inputs?: { [key: string]: string[] },
3
+ isDirected?: boolean;
4
+ }
5
+
6
+ export default IGraphOptions;
@@ -0,0 +1,318 @@
1
+ import {describe, it, expect, jest} from '@jest/globals';
2
+ import faker from 'faker';
3
+
4
+ import Graph from './';
5
+ import GraphNodeNotFoundError from './GraphNodeNotFoundError';
6
+
7
+ describe('Graph', () => {
8
+ it('Should create an empty graph without problems', () => {
9
+ const graph = new Graph();
10
+
11
+ expect(graph.data).toEqual({});
12
+ expect(graph.size).toBe(0);
13
+ });
14
+
15
+ it('Should create a filled graph without problems', () => {
16
+ const graph = new Graph({
17
+ inputs: {
18
+ A: ['B', 'C', 'D'],
19
+ B: ['A', 'C'],
20
+ C: ['A', 'B'],
21
+ D: ['A'],
22
+ },
23
+ });
24
+
25
+ expect(graph.data).toEqual({
26
+ A: ['B', 'C', 'D'],
27
+ B: ['A', 'C'],
28
+ C: ['A', 'B'],
29
+ D: ['A'],
30
+ });
31
+ expect(graph.size).toBe(4);
32
+ });
33
+
34
+ it('Should throw an error when receive a link with a non existent node', () => {
35
+ expect(() => {
36
+ // eslint-disable-next-line no-new
37
+ new Graph({
38
+ inputs: {
39
+ A: ['B'],
40
+ B: ['C'],
41
+ C: [],
42
+ },
43
+ });
44
+ }).not.toThrow(new GraphNodeNotFoundError('C'));
45
+
46
+ expect(() => {
47
+ // eslint-disable-next-line no-new
48
+ new Graph({
49
+ inputs: {
50
+ A: ['B'],
51
+ B: ['C'],
52
+ },
53
+ });
54
+ }).toThrow(new GraphNodeNotFoundError('C'));
55
+ });
56
+
57
+ describe('.isDirected', () => {
58
+ it('Should be true when the graph is directed', () => {
59
+ const graph = new Graph({
60
+ isDirected: true,
61
+ });
62
+
63
+ expect(graph.isDirected).toBe(true);
64
+ });
65
+
66
+ it('Should be false when the graph is not directed', () => {
67
+ const graph = new Graph({
68
+ isDirected: false,
69
+ });
70
+
71
+ expect(graph.isDirected).toBe(false);
72
+ });
73
+ });
74
+
75
+ describe('.nodes', () => {
76
+ it('Should be the graph nodes', () => {
77
+ const graph = new Graph({
78
+ inputs: {
79
+ A: ['B'],
80
+ B: ['C'],
81
+ C: [],
82
+ },
83
+ });
84
+
85
+ expect(graph.nodes.sort()).toEqual(['A', 'B', 'C'].sort());
86
+ });
87
+ });
88
+
89
+ describe('.edges', () => {
90
+ it('Should be the correct edges when the graph is directed', () => {
91
+ const graph = new Graph({
92
+ inputs: {
93
+ A: ['B'],
94
+ B: ['A', 'C'],
95
+ C: [],
96
+ },
97
+ isDirected: true,
98
+ });
99
+
100
+ expect(graph.edges).toEqual([
101
+ ['A', 'B'],
102
+ ['B', 'A'],
103
+ ['B', 'C'],
104
+ ]);
105
+ });
106
+
107
+ it('Should be the correct edges when the graph is not directed', () => {
108
+ const graph = new Graph({
109
+ inputs: {
110
+ A: ['B'],
111
+ B: ['A', 'C'],
112
+ C: ['B'],
113
+ },
114
+ isDirected: false,
115
+ });
116
+
117
+ expect(graph.edges).toEqual([
118
+ ['A', 'B'],
119
+ ['B', 'C'],
120
+ ]);
121
+ });
122
+ });
123
+
124
+ describe('.insert()', () => {
125
+ it('Should insert a new node in the graph', () => {
126
+ const graph = new Graph();
127
+ graph.insert('A');
128
+
129
+ expect(graph.data).toEqual({
130
+ A: [],
131
+ });
132
+ expect(graph.size).toBe(1);
133
+ });
134
+
135
+ it('Should return the inserted node', () => {
136
+ const graph = new Graph();
137
+ const returned = graph.insert('A');
138
+
139
+ expect(returned).toBe('A');
140
+ });
141
+
142
+ it('Should change nothing if the node is already in the graph', () => {
143
+ const graph = new Graph({
144
+ inputs: {
145
+ A: ['B'],
146
+ B: ['A'],
147
+ },
148
+ });
149
+ graph.insert('A');
150
+
151
+ expect(graph.data).toEqual({
152
+ A: ['B'],
153
+ B: ['A'],
154
+ });
155
+ expect(graph.size).toBe(2);
156
+ });
157
+
158
+ it('Should return null when the node is already in the graph', () => {
159
+ const graph = new Graph({
160
+ inputs: {
161
+ A: ['B'],
162
+ B: ['A'],
163
+ },
164
+ });
165
+ const returned = graph.insert('A');
166
+
167
+ expect(returned).toBeNull();
168
+ });
169
+ });
170
+
171
+ describe('.connect()', () => {
172
+ it('Should connect two different nodes', () => {
173
+ const graph = new Graph({
174
+ inputs: {
175
+ A: [],
176
+ B: [],
177
+ },
178
+ });
179
+ graph.connect('A', 'B');
180
+
181
+ expect(graph.data).toEqual({
182
+ A: ['B'],
183
+ B: ['A'],
184
+ });
185
+ expect(graph.size).toBe(2);
186
+ });
187
+
188
+ it('Should throw an error when the first node is not in the graph', () => {
189
+ const graph = new Graph({
190
+ inputs: {
191
+ B: [],
192
+ },
193
+ });
194
+
195
+ expect(() => {
196
+ graph.connect('A', 'B');
197
+ }).toThrow(new GraphNodeNotFoundError('A'));
198
+ });
199
+
200
+ it('Should throw an error when the second node is not in the graph', () => {
201
+ const graph = new Graph({
202
+ inputs: {
203
+ A: [],
204
+ },
205
+ });
206
+
207
+ expect(() => {
208
+ graph.connect('A', 'B');
209
+ }).toThrow(new GraphNodeNotFoundError('B'));
210
+ });
211
+
212
+ it('Should connect only the first node to the second when the graph is directed', () => {
213
+ const graph = new Graph({
214
+ isDirected: true,
215
+ inputs: {
216
+ A: [],
217
+ B: [],
218
+ },
219
+ });
220
+ graph.connect('A', 'B');
221
+
222
+ expect(graph.data).toEqual({
223
+ A: ['B'],
224
+ B: [],
225
+ });
226
+ expect(graph.size).toBe(2);
227
+ });
228
+
229
+ it('Should return the inserted edge', () => {
230
+ const graph = new Graph({
231
+ isDirected: true,
232
+ inputs: {
233
+ A: [],
234
+ B: [],
235
+ },
236
+ });
237
+ const returned = graph.connect('A', 'B');
238
+
239
+ expect(returned).toEqual(['A', 'B']);
240
+ });
241
+ });
242
+
243
+ describe('.breadthFirstSearch()', () => {
244
+ it('Should traverse all the graph', () => {
245
+ const keys = new Array(10).fill(undefined).map(faker.datatype.string);
246
+ const graph = new Graph();
247
+
248
+ for (const key of keys) {
249
+ graph.insert(key);
250
+ }
251
+
252
+ for (const key1 of keys) {
253
+ for (const key2 of keys) {
254
+ if (Math.random() > 0.5) {
255
+ graph.connect(key1, key2);
256
+ }
257
+ }
258
+ }
259
+
260
+ const callback = jest.fn();
261
+ graph.breadthFirstSearch(keys[0], callback);
262
+
263
+ expect(callback).toBeCalledTimes(keys.length);
264
+ for (const key of keys) {
265
+ expect(callback).toBeCalledWith(key);
266
+ }
267
+ });
268
+
269
+ it('Should throw an error when the start node is not in the graph', () => {
270
+ const graph = new Graph({
271
+ inputs: {
272
+ A: ['B'],
273
+ B: ['A'],
274
+ },
275
+ });
276
+
277
+ expect(() => {
278
+ graph.breadthFirstSearch('C', jest.fn());
279
+ }).toThrow(new GraphNodeNotFoundError('C'));
280
+ });
281
+ });
282
+
283
+ describe('.getDistancesFrom()', () => {
284
+ it('Should return the distances from the origin to the others nodes', () => {
285
+ const graph = new Graph({
286
+ inputs: {
287
+ A: ['C', 'D', 'E'],
288
+ B: ['C', 'E'],
289
+ C: ['A', 'B'],
290
+ D: ['A'],
291
+ E: ['A', 'B'],
292
+ },
293
+ });
294
+ const returned = graph.getDistancesFrom('B');
295
+
296
+ expect(returned).toEqual({
297
+ A: 2,
298
+ B: 0,
299
+ C: 1,
300
+ D: 3,
301
+ E: 1,
302
+ });
303
+ });
304
+
305
+ it('Should throw an error when the origin node is not in the graph', () => {
306
+ const graph = new Graph({
307
+ inputs: {
308
+ A: ['B'],
309
+ B: ['A'],
310
+ },
311
+ });
312
+
313
+ expect(() => {
314
+ graph.getDistancesFrom('C');
315
+ }).toThrow(new GraphNodeNotFoundError('C'));
316
+ });
317
+ });
318
+ });
@@ -0,0 +1,170 @@
1
+ import GraphNodeNotFoundError from './GraphNodeNotFoundError';
2
+ import GraphSearchNodeStates from './GraphSearchNodeStates';
3
+ import IGraph from './IGraph';
4
+ import IGraphOptions from './IGraphOptions';
5
+
6
+ import Dictionary from '../Dictionary';
7
+ import Queue from '../Queue';
8
+ import Set from '../Set';
9
+
10
+ export default class Graph implements IGraph {
11
+ private _isDirected: boolean;
12
+ private _data: Dictionary<Set<string>>;
13
+
14
+ constructor({inputs = {}, isDirected = false}: IGraphOptions = {inputs: {}, isDirected: false}) {
15
+ this._isDirected = isDirected;
16
+ this._data = new Dictionary<Set<string>>();
17
+
18
+ const nodes = Object.keys(inputs);
19
+ for (const node of nodes) {
20
+ this.insert(node);
21
+ }
22
+
23
+ for (const node of nodes) {
24
+ for (const value of inputs[node]) {
25
+ this.connect(node, value);
26
+ }
27
+ }
28
+ }
29
+
30
+ get isDirected(): boolean {
31
+ return this._isDirected;
32
+ }
33
+
34
+ get data(): { [key: string]: string[]; } {
35
+ return Object.entries(this._data.data)
36
+ .reduce((accumulator, [key, value]) => ({
37
+ ...accumulator,
38
+ [key]: value.data,
39
+ }), {});
40
+ }
41
+
42
+ get size(): number {
43
+ return this._data.size;
44
+ }
45
+
46
+ get nodes(): string[] {
47
+ return [...this._data.keys];
48
+ }
49
+
50
+ get edges(): [string, string][] {
51
+ if (this.isDirected) {
52
+ return Object.entries(this.data)
53
+ .flatMap((
54
+ [node, links]) => links.map<[string, string]>(link => [node, link]),
55
+ );
56
+ }
57
+
58
+ return Object.entries(this.data)
59
+ .reduce<[string, string][]>((accumulator, [node, links]) => {
60
+ const edges = links
61
+ .map<[string, string]>(link => [node, link])
62
+ .filter(edge => !accumulator.find(item => item.includes(edge[0]) && item.includes(edge[1])));
63
+
64
+ return [
65
+ ...accumulator,
66
+ ...edges,
67
+ ];
68
+ }, []);
69
+ }
70
+
71
+ public insert(node: string): string | null {
72
+ if (this._data.hasKey(node)) {
73
+ return null;
74
+ }
75
+
76
+ this._data.set(node, new Set<string>());
77
+
78
+ return node;
79
+ }
80
+
81
+ public connect(node1: string, node2: string): [string, string] {
82
+ if (!this._data.hasKey(node1)) {
83
+ throw new GraphNodeNotFoundError(node1);
84
+ }
85
+
86
+ if (!this._data.hasKey(node2)) {
87
+ throw new GraphNodeNotFoundError(node2);
88
+ }
89
+
90
+ this._data.get(node1)?.add(node2);
91
+
92
+ if (!this._isDirected) {
93
+ this._data.get(node2)?.add(node1);
94
+ }
95
+
96
+ return [node1, node2];
97
+ }
98
+
99
+ public breadthFirstSearch(startNode: string, callback: (_node: string) => void): void {
100
+ if (!this._data.hasKey(startNode)) {
101
+ throw new GraphNodeNotFoundError(startNode);
102
+ }
103
+
104
+ const {nodes} = this;
105
+ const states: { [key: string]: GraphSearchNodeStates } = nodes.reduce((accumulator, node) => ({
106
+ ...accumulator,
107
+ [node]: GraphSearchNodeStates.UNEXPLORED,
108
+ }), {});
109
+
110
+ const queue = new Queue<string>();
111
+ queue.enqueue(startNode);
112
+
113
+ while (!queue.isEmpty) {
114
+ const current = queue.dequeue() || '';
115
+
116
+ if (current) {
117
+ states[current] = GraphSearchNodeStates.DISCOVERED;
118
+ const neighbors = this._data.get(current)?.data || [];
119
+ const unexploredNeighbors = neighbors.filter(neighbor => states[neighbor] === GraphSearchNodeStates.UNEXPLORED);
120
+
121
+ for (const neighbor of unexploredNeighbors) {
122
+ states[neighbor] = GraphSearchNodeStates.DISCOVERED;
123
+ queue.enqueue(neighbor);
124
+ }
125
+ }
126
+
127
+ states[current] = GraphSearchNodeStates.EXPLORED;
128
+ callback(current);
129
+ }
130
+ }
131
+
132
+ public getDistancesFrom(node: string): { [key: string]: number; } {
133
+ if (!this._data.hasKey(node)) {
134
+ throw new GraphNodeNotFoundError(node);
135
+ }
136
+
137
+ const {nodes} = this;
138
+ const states: { [key: string]: GraphSearchNodeStates } = nodes.reduce((accumulator, node) => ({
139
+ ...accumulator,
140
+ [node]: GraphSearchNodeStates.UNEXPLORED,
141
+ }), {});
142
+ const distances: { [key: string]: number } = nodes.reduce((accumulator, node) => ({
143
+ ...accumulator,
144
+ [node]: 0,
145
+ }), {});
146
+
147
+ const queue = new Queue<string>();
148
+ queue.enqueue(node);
149
+
150
+ while (!queue.isEmpty) {
151
+ const current = queue.dequeue() || '';
152
+
153
+ if (current) {
154
+ states[current] = GraphSearchNodeStates.DISCOVERED;
155
+ const neighbors = this._data.get(current)?.data || [];
156
+ const unexploredNeighbors = neighbors.filter(neighbor => states[neighbor] === GraphSearchNodeStates.UNEXPLORED);
157
+
158
+ for (const neighbor of unexploredNeighbors) {
159
+ states[neighbor] = GraphSearchNodeStates.DISCOVERED;
160
+ distances[neighbor] = distances[current] + 1;
161
+ queue.enqueue(neighbor);
162
+ }
163
+ }
164
+
165
+ states[current] = GraphSearchNodeStates.EXPLORED;
166
+ }
167
+
168
+ return distances;
169
+ }
170
+ }
@@ -0,0 +1,11 @@
1
+ import IHashTableData from './IHashTableData';
2
+
3
+ interface IHashTable<T> {
4
+ get data(): IHashTableData<T>;
5
+ get size(): number;
6
+ put(_key: string, _value: T): T;
7
+ get(_key: string): T | null;
8
+ remove(_key: string): T | null;
9
+ }
10
+
11
+ export default IHashTable;
@@ -0,0 +1,5 @@
1
+ interface IHashTableData<T> {
2
+ [key: number]: T;
3
+ }
4
+
5
+ export default IHashTableData;
@@ -0,0 +1,5 @@
1
+ interface IHashTableInputs<T> {
2
+ [key: string]: T;
3
+ }
4
+
5
+ export default IHashTableInputs;
@@ -0,0 +1,5 @@
1
+ interface IHashTableOptions {
2
+ maxSize?: number;
3
+ }
4
+
5
+ export default IHashTableOptions;
@@ -0,0 +1,136 @@
1
+ import {describe, it, expect} from '@jest/globals';
2
+
3
+ import HashTable from './';
4
+
5
+ describe('HashTable', () => {
6
+ it('Should create an empty hash table without problems', () => {
7
+ const hashTable = new HashTable<number>();
8
+
9
+ expect(hashTable.data).toEqual({});
10
+ expect(hashTable.size).toBe(0);
11
+ });
12
+
13
+ it('Should create an filled hash table without problems', () => {
14
+ const hashTable = new HashTable<number>({
15
+ first: 1,
16
+ second: 2,
17
+ third: 3,
18
+ fourth: 4,
19
+ });
20
+
21
+ expect(hashTable.data).toEqual({
22
+ 36: 2,
23
+ 39: 3,
24
+ 52: 1,
25
+ 64: 4,
26
+ });
27
+ expect(hashTable.size).toBe(4);
28
+ });
29
+
30
+ describe('.put()', () => {
31
+ it('Should insert a new element in hash table', () => {
32
+ const hashTable = new HashTable();
33
+ hashTable.put('first', 1);
34
+
35
+ expect(hashTable.data).toEqual({
36
+ 52: 1,
37
+ });
38
+ expect(hashTable.size).toBe(1);
39
+ });
40
+
41
+ it('Should return the inserted element', () => {
42
+ const hashTable = new HashTable();
43
+ const returned = hashTable.put('first', 1);
44
+
45
+ expect(returned).toEqual(1);
46
+ });
47
+ });
48
+
49
+ describe('.get()', () => {
50
+ it('Should return the element of the key', () => {
51
+ const hashTable = new HashTable({
52
+ first: 1,
53
+ });
54
+ const returned = hashTable.get('first');
55
+
56
+ expect(returned).toBe(1);
57
+ });
58
+
59
+ it('Should return null if the key does not exists in the hash table', () => {
60
+ const hashTable = new HashTable({
61
+ first: 1,
62
+ });
63
+ const returned = hashTable.get('second');
64
+
65
+ expect(returned).toBeNull();
66
+ });
67
+ });
68
+
69
+ describe('.remove()', () => {
70
+ it('Should remove an existent key value pair', () => {
71
+ const hashTable = new HashTable<number>({
72
+ first: 1,
73
+ second: 2,
74
+ });
75
+ hashTable.remove('second');
76
+
77
+ expect(hashTable.data).toEqual({
78
+ 52: 1,
79
+ });
80
+ expect(hashTable.size).toBe(1);
81
+ });
82
+
83
+ it('Should not change the hash table when receive a non existent key', () => {
84
+ const hashTable = new HashTable<number>({
85
+ first: 1,
86
+ });
87
+ hashTable.remove('second');
88
+
89
+ expect(hashTable.data).toEqual({
90
+ 52: 1,
91
+ });
92
+ expect(hashTable.size).toBe(1);
93
+ });
94
+
95
+ it('Should return the removed value', () => {
96
+ const hashTable = new HashTable({
97
+ first: 1,
98
+ second: 2,
99
+ });
100
+ const returned = hashTable.remove('second');
101
+
102
+ expect(returned).toBe(2);
103
+ });
104
+
105
+ it('Should return null when receive a non existent key', () => {
106
+ const hashTable = new HashTable({
107
+ first: 1,
108
+ });
109
+ const returned = hashTable.remove('second');
110
+
111
+ expect(returned).toBeNull();
112
+ });
113
+ });
114
+
115
+ describe('Conversion to primitive', () => {
116
+ it('Should return a separated by comma key value pairs in string conversion', () => {
117
+ const hashTable = new HashTable({
118
+ first: 1,
119
+ second: 2,
120
+ });
121
+ const string = String(hashTable);
122
+
123
+ expect(string).toBe('[ 36 => 2, 52 => 1 ]');
124
+ });
125
+
126
+ it('Should return the hash table size in number conversion', () => {
127
+ const hashTable = new HashTable({
128
+ first: 1,
129
+ second: 2,
130
+ });
131
+ const number = Number(hashTable);
132
+
133
+ expect(number).toBe(2);
134
+ });
135
+ });
136
+ });