1
+ class Comparator {
2
+ /**
3
+ * Constructor.
4
+ * @param {function(a: *, b: *) } [compareFunction] - It may be custom compare function that, let's
5
+ * say may compare custom objects together.
6
+ */
7
+ constructor ( compareFunction ) {
8
+ this . compare = compareFunction || Comparator . defaultCompareFunction ;
9
+ }
10
+
11
+ /**
12
+ * Default comparison function. It just assumes that "a" and "b" are strings or numbers.
13
+ * @param {(string|number) } a
14
+ * @param {(string|number) } b
15
+ * @returns {number }
16
+ */
17
+ static defaultCompareFunction ( a , b ) {
18
+ if ( a === b ) {
19
+ return 0 ;
20
+ }
21
+
22
+ return a < b ? - 1 : 1 ;
23
+ }
24
+
25
+ /**
26
+ * Checks if two variables are equal.
27
+ * @param {* } a
28
+ * @param {* } b
29
+ * @return {boolean }
30
+ */
31
+ equal ( a , b ) {
32
+ return this . compare ( a , b ) === 0 ;
33
+ }
34
+
35
+ /**
36
+ * Checks if variable "a" is less than "b".
37
+ * @param {* } a
38
+ * @param {* } b
39
+ * @return {boolean }
40
+ */
41
+ lessThan ( a , b ) {
42
+ return this . compare ( a , b ) < 0 ;
43
+ }
44
+
45
+ /**
46
+ * Checks if variable "a" is greater than "b".
47
+ * @param {* } a
48
+ * @param {* } b
49
+ * @return {boolean }
50
+ */
51
+ greaterThan ( a , b ) {
52
+ return this . compare ( a , b ) > 0 ;
53
+ }
54
+
55
+ /**
56
+ * Checks if variable "a" is less than or equal to "b".
57
+ * @param {* } a
58
+ * @param {* } b
59
+ * @return {boolean }
60
+ */
61
+ lessThanOrEqual ( a , b ) {
62
+ return this . lessThan ( a , b ) || this . equal ( a , b ) ;
63
+ }
64
+
65
+ /**
66
+ * Checks if variable "a" is greater than or equal to "b".
67
+ * @param {* } a
68
+ * @param {* } b
69
+ * @return {boolean }
70
+ */
71
+ greaterThanOrEqual ( a , b ) {
72
+ return this . greaterThan ( a , b ) || this . equal ( a , b ) ;
73
+ }
74
+
75
+ /**
76
+ * Reverses the comparison order.
77
+ */
78
+ reverse ( ) {
79
+ const compareOriginal = this . compare ;
80
+ this . compare = ( a , b ) => compareOriginal ( b , a ) ;
81
+ }
82
+ }
83
+
84
+ class LinkedListNode {
85
+ constructor ( value , next = null ) {
86
+ this . value = value ;
87
+ this . next = next ;
88
+ }
89
+
90
+ toString ( callback ) {
91
+ return callback ? callback ( this . value ) : `${ this . value } ` ;
92
+ }
93
+ }
94
+
95
+ class LinkedList {
96
+ /**
97
+ * @param {Function } [comparatorFunction]
98
+ */
99
+ constructor ( comparatorFunction ) {
100
+ /** @var LinkedListNode */
101
+ this . head = null ;
102
+
103
+ /** @var LinkedListNode */
104
+ this . tail = null ;
105
+
106
+ this . compare = new Comparator ( comparatorFunction ) ;
107
+ }
108
+
109
+ /**
110
+ * @param {* } value
111
+ * @return {LinkedList }
112
+ */
113
+ prepend ( value ) {
114
+ // Make new node to be a head.
115
+ const newNode = new LinkedListNode ( value , this . head ) ;
116
+ this . head = newNode ;
117
+
118
+ // If there is no tail yet let's make new node a tail.
119
+ if ( ! this . tail ) {
120
+ this . tail = newNode ;
121
+ }
122
+
123
+ return this ;
124
+ }
125
+
126
+ /**
127
+ * @param {* } value
128
+ * @return {LinkedList }
129
+ */
130
+ append ( value ) {
131
+ const newNode = new LinkedListNode ( value ) ;
132
+
133
+ // If there is no head yet let's make new node a head.
134
+ if ( ! this . head ) {
135
+ this . head = newNode ;
136
+ this . tail = newNode ;
137
+
138
+ return this ;
139
+ }
140
+
141
+ // Attach new node to the end of linked list.
142
+ this . tail . next = newNode ;
143
+ this . tail = newNode ;
144
+
145
+ return this ;
146
+ }
147
+
148
+ /**
149
+ * @param {* } value
150
+ * @param {number } index
151
+ * @return {LinkedList }
152
+ */
153
+ insert ( value , rawIndex ) {
154
+ const index = rawIndex < 0 ? 0 : rawIndex ;
155
+ if ( index === 0 ) {
156
+ this . prepend ( value ) ;
157
+ } else {
158
+ let count = 1 ;
159
+ let currentNode = this . head ;
160
+ const newNode = new LinkedListNode ( value ) ;
161
+ while ( currentNode ) {
162
+ if ( count === index ) break ;
163
+ currentNode = currentNode . next ;
164
+ count += 1 ;
165
+ }
166
+ if ( currentNode ) {
167
+ newNode . next = currentNode . next ;
168
+ currentNode . next = newNode ;
169
+ } else {
170
+ if ( this . tail ) {
171
+ this . tail . next = newNode ;
172
+ this . tail = newNode ;
173
+ } else {
174
+ this . head = newNode ;
175
+ this . tail = newNode ;
176
+ }
177
+ }
178
+ }
179
+ return this ;
180
+ }
181
+
182
+ /**
183
+ * @param {* } value
184
+ * @return {LinkedListNode }
185
+ */
186
+ delete ( value ) {
187
+ if ( ! this . head ) {
188
+ return null ;
189
+ }
190
+
191
+ let deletedNode = null ;
192
+
193
+ // If the head must be deleted then make next node that is different
194
+ // from the head to be a new head.
195
+ while ( this . head && this . compare . equal ( this . head . value , value ) ) {
196
+ deletedNode = this . head ;
197
+ this . head = this . head . next ;
198
+ }
199
+
200
+ let currentNode = this . head ;
201
+
202
+ if ( currentNode !== null ) {
203
+ // If next node must be deleted then make next node to be a next next one.
204
+ while ( currentNode . next ) {
205
+ if ( this . compare . equal ( currentNode . next . value , value ) ) {
206
+ deletedNode = currentNode . next ;
207
+ currentNode . next = currentNode . next . next ;
208
+ } else {
209
+ currentNode = currentNode . next ;
210
+ }
211
+ }
212
+ }
213
+
214
+ // Check if tail must be deleted.
215
+ if ( this . compare . equal ( this . tail . value , value ) ) {
216
+ this . tail = currentNode ;
217
+ }
218
+
219
+ return deletedNode ;
220
+ }
221
+
222
+ /**
223
+ * @param {Object } findParams
224
+ * @param {* } findParams.value
225
+ * @param {function } [findParams.callback]
226
+ * @return {LinkedListNode }
227
+ */
228
+ find ( { value = undefined , callback = undefined } ) {
229
+ if ( ! this . head ) {
230
+ return null ;
231
+ }
232
+
233
+ let currentNode = this . head ;
234
+
235
+ while ( currentNode ) {
236
+ // If callback is specified then try to find node by callback.
237
+ if ( callback && callback ( currentNode . value ) ) {
238
+ return currentNode ;
239
+ }
240
+
241
+ // If value is specified then try to compare by value..
242
+ if ( value !== undefined && this . compare . equal ( currentNode . value , value ) ) {
243
+ return currentNode ;
244
+ }
245
+
246
+ currentNode = currentNode . next ;
247
+ }
248
+
249
+ return null ;
250
+ }
251
+
252
+ /**
253
+ * @return {LinkedListNode }
254
+ */
255
+ deleteTail ( ) {
256
+ const deletedTail = this . tail ;
257
+
258
+ if ( this . head === this . tail ) {
259
+ // There is only one node in linked list.
260
+ this . head = null ;
261
+ this . tail = null ;
262
+
263
+ return deletedTail ;
264
+ }
265
+
266
+ // If there are many nodes in linked list...
267
+
268
+ // Rewind to the last node and delete "next" link for the node before the last one.
269
+ let currentNode = this . head ;
270
+ while ( currentNode . next ) {
271
+ if ( ! currentNode . next . next ) {
272
+ currentNode . next = null ;
273
+ } else {
274
+ currentNode = currentNode . next ;
275
+ }
276
+ }
277
+
278
+ this . tail = currentNode ;
279
+
280
+ return deletedTail ;
281
+ }
282
+
283
+ /**
284
+ * @return {LinkedListNode }
285
+ */
286
+ deleteHead ( ) {
287
+ if ( ! this . head ) {
288
+ return null ;
289
+ }
290
+
291
+ const deletedHead = this . head ;
292
+
293
+ if ( this . head . next ) {
294
+ this . head = this . head . next ;
295
+ } else {
296
+ this . head = null ;
297
+ this . tail = null ;
298
+ }
299
+
300
+ return deletedHead ;
301
+ }
302
+
303
+ /**
304
+ * @param {*[] } values - Array of values that need to be converted to linked list.
305
+ * @return {LinkedList }
306
+ */
307
+ fromArray ( values ) {
308
+ values . forEach ( ( value ) => this . append ( value ) ) ;
309
+
310
+ return this ;
311
+ }
312
+
313
+ /**
314
+ * @return {LinkedListNode[] }
315
+ */
316
+ toArray ( ) {
317
+ const nodes = [ ] ;
318
+
319
+ let currentNode = this . head ;
320
+ while ( currentNode ) {
321
+ nodes . push ( currentNode ) ;
322
+ currentNode = currentNode . next ;
323
+ }
324
+
325
+ return nodes ;
326
+ }
327
+
328
+ /**
329
+ * @param {function } [callback]
330
+ * @return {string }
331
+ */
332
+ toString ( callback ) {
333
+ return this . toArray ( ) . map ( ( node ) => node . toString ( callback ) ) . toString ( ) ;
334
+ }
335
+
336
+ /**
337
+ * Reverse a linked list.
338
+ * @returns {LinkedList }
339
+ */
340
+ reverse ( ) {
341
+ let currNode = this . head ;
342
+ let prevNode = null ;
343
+ let nextNode = null ;
344
+
345
+ while ( currNode ) {
346
+ // Store next node.
347
+ nextNode = currNode . next ;
348
+
349
+ // Change next node of the current node so it would link to previous node.
350
+ currNode . next = prevNode ;
351
+
352
+ // Move prevNode and currNode nodes one step forward.
353
+ prevNode = currNode ;
354
+ currNode = nextNode ;
355
+ }
356
+
357
+ // Reset head and tail.
358
+ this . tail = this . head ;
359
+ this . head = prevNode ;
360
+
361
+ return this ;
362
+ }
363
+ }
0 commit comments