@@ -16,13 +16,6 @@ class LinkedList {
16
16
}
17
17
// end::constructor[]
18
18
19
- /**
20
- * Alias for size
21
- */
22
- get length ( ) {
23
- return this . size ;
24
- }
25
-
26
19
// tag::addFirst[]
27
20
/**
28
21
* Adds element to the begining of the list. Similar to Array.unshift
@@ -34,9 +27,9 @@ class LinkedList {
34
27
35
28
newNode . next = this . first ;
36
29
37
- if ( this . first ) {
38
- this . first . previous = newNode ;
39
- } else {
30
+ if ( this . first ) { // check if first node exists (list not empty)
31
+ this . first . previous = newNode ; // <1>
32
+ } else { // if list is empty, first & last will point to newNode.
40
33
this . last = newNode ;
41
34
}
42
35
@@ -59,11 +52,11 @@ class LinkedList {
59
52
addLast ( value ) {
60
53
const newNode = new Node ( value ) ;
61
54
62
- if ( this . first ) {
55
+ if ( this . first ) { // check if first node exists (list not empty)
63
56
newNode . previous = this . last ;
64
57
this . last . next = newNode ;
65
58
this . last = newNode ;
66
- } else {
59
+ } else { // if list is empty, first & last will point to newNode.
67
60
this . first = newNode ;
68
61
this . last = newNode ;
69
62
}
@@ -78,195 +71,157 @@ class LinkedList {
78
71
/**
79
72
* Insert new element at the given position (index)
80
73
*
74
+ * Runtime: O(n)
75
+ *
81
76
* @param {any } value new node's value
82
77
* @param {Number } position position to insert element
83
- * @returns {Node } new node or 'undefined' if the index is out of bound.
78
+ * @returns {Node|undefined } new node or 'undefined' if the index is out of bound.
84
79
*/
85
- add ( value , position = 0 ) {
86
- if ( position === 0 ) { // <1>
87
- return this . addFirst ( value ) ;
88
- }
80
+ addAt ( value , position = 0 ) {
81
+ if ( position === 0 ) return this . addFirst ( value ) ; // <1>
82
+ if ( position === this . size ) return this . addLast ( value ) ; // <2>
89
83
90
- if ( position === this . size ) { // <2>
91
- return this . addLast ( value ) ;
92
- }
93
84
// Adding element in the middle
94
- const current = this . get ( position ) ;
95
- if ( current ) {
96
- const newNode = new Node ( value ) ; // <3>
97
- newNode . previous = current . previous ; // <4>
98
- newNode . next = current ; // <5>
99
-
100
- current . previous . next = newNode ; // <6>
101
- current . previous = newNode ; // <7>
102
- this . size += 1 ;
103
- return newNode ;
104
- }
105
-
106
- return undefined ; // out of bound index
85
+ const current = this . findBy ( { index : position } ) . node ;
86
+ if ( ! current ) return undefined ; // out of bound index
87
+
88
+ const newNode = new Node ( value ) ; // <3>
89
+ newNode . previous = current . previous ; // <4>
90
+ newNode . next = current ; // <5>
91
+ current . previous . next = newNode ; // <6>
92
+ current . previous = newNode ; // <7>
93
+ this . size += 1 ;
94
+ return newNode ;
107
95
}
108
96
// end::addMiddle[]
109
97
110
98
// tag::searchByValue[]
111
99
/**
100
+ * @deprecated use findBy
112
101
* Search by value. It finds first occurrence of
113
- * the element matching the value.
102
+ * the position of element matching the value.
103
+ * Similar to Array.indexOf.
104
+ *
114
105
* Runtime: O(n)
106
+ *
115
107
* @example : assuming a linked list with: a -> b -> c
116
- * linkedList.indexOf ('b') // ↪️ 1
117
- * linkedList.indexOf ('z') // ↪️ undefined
108
+ * linkedList.getIndexByValue ('b') // ↪️ 1
109
+ * linkedList.getIndexByValue ('z') // ↪️ undefined
118
110
* @param {any } value
119
111
* @returns {number } return index or undefined
120
112
*/
121
- indexOf ( value ) {
122
- return this . find ( ( current , position ) => {
123
- if ( current . value === value ) {
124
- return position ;
125
- }
126
- return undefined ;
127
- } ) ;
113
+ getIndexByValue ( value ) {
114
+ return this . findBy ( { value } ) . index ;
128
115
}
129
116
// end::searchByValue[]
130
117
131
118
// tag::searchByIndex[]
132
119
/**
120
+ * @deprecated use findBy directly
133
121
* Search by index
134
122
* Runtime: O(n)
135
123
* @example : assuming a linked list with: a -> b -> c
136
124
* linkedList.get(1) // ↪️ 'b'
137
125
* linkedList.get(40) // ↪️ undefined
138
126
* @param {Number } index position of the element
139
- * @returns {Node } element at the specified position in this list.
127
+ * @returns {Node|undefined } element at the specified position in
128
+ * this list or undefined if was not found.
140
129
*/
141
130
get ( index = 0 ) {
142
- return this . find ( ( current , position ) => {
143
- if ( position === index ) {
144
- return current ;
145
- }
146
- return undefined ;
147
- } ) ;
131
+ return this . findBy ( { index } ) . node ;
148
132
}
149
133
// end::searchByIndex[]
150
134
151
135
// tag::find[]
152
136
/**
153
- * Iterate through the list until callback returns a truthy value
154
- * @example see #get and #indexOf
155
- * @param {Function } callback evaluates current node and index.
156
- * If any value other than undefined it's returned it will stop the search.
157
- * @returns {any } callbacks's return value or undefined
137
+ * Find by index or by value, whichever happens first.
138
+ * Runtime: O(n)
139
+ * @example
140
+ * this.findBy({ index: 10 }).node; // node at index 10.
141
+ * this.findBy({ value: 10 }).node; // node with value 10.
142
+ * this.findBy({ value: 10 }).index; // node's index with value 10.
143
+ *
144
+ * @param {Object } params - The search params
145
+ * @param {number } params.index - The index/position to search for.
146
+ * @param {any } params.value - The value to search for.
147
+ * @returns {{node: any, index: number} }
158
148
*/
159
- find ( callback ) {
149
+ findBy ( { value , index = Infinity } = { } ) {
160
150
for ( let current = this . first , position = 0 ; // <1>
161
- current ; // <2>
151
+ current && position <= index ; // <2>
162
152
position += 1 , current = current . next ) { // <3>
163
- const result = callback ( current , position ) ; // <4>
164
-
165
- if ( result !== undefined ) {
166
- return result ; // <5>
153
+ if ( position === index || value === current . value ) { // <4>
154
+ return { node : current , index : position } ; // <5>
167
155
}
168
156
}
169
- return undefined ; // not found
157
+ return { } ; // not found
170
158
}
171
159
// end::find[]
172
160
173
161
174
162
// tag::removeFirst[]
175
163
/**
176
164
* Removes element from the start of the list (head/root).
177
- * Similar to Array.shift
165
+ * Similar to Array.shift().
178
166
* Runtime: O(1)
179
167
* @returns {any } the first element's value which was removed.
180
168
*/
181
169
removeFirst ( ) {
170
+ if ( ! this . first ) return null ; // Check if list is already empty.
182
171
const head = this . first ;
183
172
184
- if ( head ) {
185
- this . first = head . next ;
186
- if ( this . first ) {
187
- this . first . previous = null ;
188
- } else {
189
- this . last = null ;
190
- }
191
- this . size -= 1 ;
173
+ this . first = head . next ; // move first pointer to the next element.
174
+ if ( this . first ) {
175
+ this . first . previous = null ;
176
+ } else { // if list has size zero, then we need to null out last.
177
+ this . last = null ;
192
178
}
193
- return head && head . value ;
179
+ this . size -= 1 ;
180
+ return head . value ;
194
181
}
195
182
// end::removeFirst[]
196
183
197
184
// tag::removeLast[]
198
185
/**
199
- * Removes element to the end of the list. Similar to Array.pop
200
- * Using the `last.previous` we can reduce the runtime from O(n) to O(1)
186
+ * Removes element to the end of the list.
187
+ * Similar to Array.pop().
201
188
* Runtime: O(1)
202
- * @returns {value } the last element's value which was removed
189
+ * @returns {any } the last element's value which was removed
203
190
*/
204
191
removeLast ( ) {
192
+ if ( ! this . last ) return null ; // Check if list is already empty.
205
193
const tail = this . last ;
206
194
207
- if ( tail ) {
208
- this . last = tail . previous ;
209
- if ( this . last ) {
210
- this . last . next = null ;
211
- } else {
212
- this . first = null ;
213
- }
214
- this . size -= 1 ;
195
+ this . last = tail . previous ;
196
+ if ( this . last ) {
197
+ this . last . next = null ;
198
+ } else { // if list has size zero, then we need to null out first.
199
+ this . first = null ;
215
200
}
216
- return tail && tail . value ;
201
+ this . size -= 1 ;
202
+ return tail . value ;
217
203
}
218
204
// end::removeLast[]
219
205
220
206
// tag::removeByPosition[]
221
207
/**
222
- * Removes the element at the specified position in this list.
208
+ * Removes the element at the given position (index) in this list.
223
209
* Runtime: O(n)
224
210
* @param {any } position
225
211
* @returns {any } the element's value at the specified position that was removed.
226
212
*/
227
213
removeByPosition ( position = 0 ) {
228
- const current = this . get ( position ) ;
229
-
230
- if ( position === 0 ) {
231
- this . removeFirst ( ) ;
232
- } else if ( position === this . size - 1 ) {
233
- this . removeLast ( ) ;
234
- } else if ( current ) {
235
- current . previous . next = current . next ;
236
- current . next . previous = current . previous ;
237
- this . size -= 1 ;
238
- }
239
-
214
+ if ( position === 0 ) return this . removeFirst ( ) ;
215
+ if ( position === this . size - 1 ) return this . removeLast ( ) ;
216
+ const current = this . findBy ( { index : position } ) . node ;
217
+ if ( ! current ) return null ;
218
+ current . previous . next = current . next ;
219
+ current . next . previous = current . previous ;
220
+ this . size -= 1 ;
240
221
return current && current . value ;
241
222
}
242
223
// end::removeByPosition[]
243
224
244
- /**
245
- * Removes the first occurrence of the specified elementt
246
- * from this list, if it is present.
247
- * Runtime: O(n)
248
- * @param {any } callbackOrIndex callback or position index to remove
249
- */
250
- remove ( callbackOrIndex ) {
251
- if ( typeof callbackOrIndex !== 'function' ) {
252
- return this . removeByPosition ( parseInt ( callbackOrIndex , 10 ) || 0 ) ;
253
- }
254
-
255
- // find desired position to remove using #find
256
- const position = this . find ( ( node , index ) => {
257
- if ( callbackOrIndex ( node , index ) ) {
258
- return index ;
259
- }
260
- return undefined ;
261
- } ) ;
262
-
263
- if ( position !== undefined ) { // zero-based position.
264
- return this . removeByPosition ( position ) ;
265
- }
266
-
267
- return false ;
268
- }
269
-
270
225
/**
271
226
* Remove element by Node
272
227
* O(1)
@@ -302,6 +257,61 @@ class LinkedList {
302
257
const parts = [ ...this ] ; // see [Symbol.iterator]()
303
258
return parts . map ( ( n ) => util . inspect ( n . node . value ) ) . join ( ' -> ' ) ;
304
259
}
260
+
261
+ /**
262
+ * Alias for size
263
+ */
264
+ get length ( ) {
265
+ return this . size ;
266
+ }
267
+
268
+ /**
269
+ * @deprecated use findBy
270
+ * Iterate through the list until callback returns a truthy value
271
+ * @example see #get and #getIndexByValue
272
+ * @param {Function } callback evaluates current node and index.
273
+ * If any value other than undefined it's returned it will stop the search.
274
+ * @returns {any } callbacks's return value or undefined
275
+ */
276
+ find ( callback ) {
277
+ for ( let current = this . first , position = 0 ; // <1>
278
+ current ; // <2>
279
+ position += 1 , current = current . next ) { // <3>
280
+ const result = callback ( current , position ) ; // <4>
281
+
282
+ if ( result !== undefined ) {
283
+ return result ; // <5>
284
+ }
285
+ }
286
+ return undefined ; // not found
287
+ }
288
+
289
+ /**
290
+ * @deprecated use removeByNode or removeByPosition
291
+ * Removes the first occurrence of the specified elementt
292
+ * from this list, if it is present.
293
+ * Runtime: O(n)
294
+ * @param {any } callbackOrIndex callback or position index to remove
295
+ */
296
+ remove ( callbackOrIndex ) {
297
+ if ( typeof callbackOrIndex !== 'function' ) {
298
+ return this . removeByPosition ( parseInt ( callbackOrIndex , 10 ) || 0 ) ;
299
+ }
300
+
301
+ // find desired position to remove using #find
302
+ const position = this . find ( ( node , index ) => {
303
+ if ( callbackOrIndex ( node , index ) ) {
304
+ return index ;
305
+ }
306
+ return undefined ;
307
+ } ) ;
308
+
309
+ if ( position !== undefined ) { // zero-based position.
310
+ return this . removeByPosition ( position ) ;
311
+ }
312
+
313
+ return false ;
314
+ }
305
315
}
306
316
307
317
// Aliases
0 commit comments