@@ -27,9 +27,9 @@ class LinkedList {
27
27
28
28
newNode . next = this . first ;
29
29
30
- if ( this . first ) {
31
- this . first . previous = newNode ;
32
- } 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.
33
33
this . last = newNode ;
34
34
}
35
35
@@ -52,11 +52,11 @@ class LinkedList {
52
52
addLast ( value ) {
53
53
const newNode = new Node ( value ) ;
54
54
55
- if ( this . first ) {
55
+ if ( this . first ) { // check if first node exists (list not empty)
56
56
newNode . previous = this . last ;
57
57
this . last . next = newNode ;
58
58
this . last = newNode ;
59
- } else {
59
+ } else { // if list is empty, first & last will point to newNode.
60
60
this . first = newNode ;
61
61
this . last = newNode ;
62
62
}
@@ -71,20 +71,18 @@ class LinkedList {
71
71
/**
72
72
* Insert new element at the given position (index)
73
73
*
74
+ * Runtime: O(n)
75
+ *
74
76
* @param {any } value new node's value
75
77
* @param {Number } position position to insert element
76
- * @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.
77
79
*/
78
- add ( value , position = 0 ) {
79
- if ( position === 0 ) { // <1>
80
- return this . addFirst ( value ) ;
81
- }
80
+ addAt ( value , position = 0 ) {
81
+ if ( position === 0 ) return this . addFirst ( value ) ; // <1>
82
+ if ( position === this . size ) return this . addLast ( value ) ; // <2>
82
83
83
- if ( position === this . size ) { // <2>
84
- return this . addLast ( value ) ;
85
- }
86
84
// Adding element in the middle
87
- const current = this . get ( position ) ;
85
+ const current = this . findBy ( { index : position } ) . node ;
88
86
if ( ! current ) return undefined ; // out of bound index
89
87
90
88
const newNode = new Node ( value ) ; // <3>
@@ -99,6 +97,7 @@ class LinkedList {
99
97
100
98
// tag::searchByValue[]
101
99
/**
100
+ * @deprecated use findBy
102
101
* Search by value. It finds first occurrence of
103
102
* the position of element matching the value.
104
103
* Similar to Array.indexOf.
@@ -112,17 +111,13 @@ class LinkedList {
112
111
* @returns {number } return index or undefined
113
112
*/
114
113
getIndexByValue ( value ) {
115
- return this . find ( ( current , position ) => {
116
- if ( current . value === value ) {
117
- return position ;
118
- }
119
- return undefined ;
120
- } ) ;
114
+ return this . findBy ( { value } ) . index ;
121
115
}
122
116
// end::searchByValue[]
123
117
124
118
// tag::searchByIndex[]
125
119
/**
120
+ * @deprecated use findBy directly
126
121
* Search by index
127
122
* Runtime: O(n)
128
123
* @example : assuming a linked list with: a -> b -> c
@@ -133,134 +128,100 @@ class LinkedList {
133
128
* this list or undefined if was not found.
134
129
*/
135
130
get ( index = 0 ) {
136
- return this . find ( ( current , position ) => {
137
- if ( position === index ) {
138
- return current ;
139
- }
140
- return undefined ;
141
- } ) ;
131
+ return this . findBy ( { index } ) . node ;
142
132
}
143
133
// end::searchByIndex[]
144
134
145
135
// tag::find[]
146
136
/**
147
- * Iterate through the list until callback returns a truthy value
148
- * @example see #get and #getIndexByValue
149
- * @param {Function } callback evaluates current node and index.
150
- * If any value other than undefined it's returned it will stop the search.
151
- * @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} }
152
148
*/
153
- find ( callback ) {
149
+ findBy ( { value , index = Infinity } = { } ) {
154
150
for ( let current = this . first , position = 0 ; // <1>
155
- current ; // <2>
151
+ current && position <= index ; // <2>
156
152
position += 1 , current = current . next ) { // <3>
157
- const result = callback ( current , position ) ; // <4>
158
-
159
- if ( result !== undefined ) {
160
- return result ; // <5>
153
+ if ( position === index || value === current . value ) { // <4>
154
+ return { node : current , index : position } ; // <5>
161
155
}
162
156
}
163
- return undefined ; // not found
157
+ return { } ; // not found
164
158
}
165
159
// end::find[]
166
160
167
161
168
162
// tag::removeFirst[]
169
163
/**
170
164
* Removes element from the start of the list (head/root).
171
- * Similar to Array.shift
165
+ * Similar to Array.shift().
172
166
* Runtime: O(1)
173
167
* @returns {any } the first element's value which was removed.
174
168
*/
175
169
removeFirst ( ) {
170
+ if ( ! this . first ) return null ; // Check if list is already empty.
176
171
const head = this . first ;
177
172
178
- if ( head ) {
179
- this . first = head . next ;
180
- if ( this . first ) {
181
- this . first . previous = null ;
182
- } else {
183
- this . last = null ;
184
- }
185
- 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 ;
186
178
}
187
- return head && head . value ;
179
+ this . size -= 1 ;
180
+ return head . value ;
188
181
}
189
182
// end::removeFirst[]
190
183
191
184
// tag::removeLast[]
192
185
/**
193
- * Removes element to the end of the list. Similar to Array.pop
194
- * 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().
195
188
* Runtime: O(1)
196
- * @returns {value } the last element's value which was removed
189
+ * @returns {any } the last element's value which was removed
197
190
*/
198
191
removeLast ( ) {
192
+ if ( ! this . last ) return null ; // Check if list is already empty.
199
193
const tail = this . last ;
200
194
201
- if ( tail ) {
202
- this . last = tail . previous ;
203
- if ( this . last ) {
204
- this . last . next = null ;
205
- } else {
206
- this . first = null ;
207
- }
208
- 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 ;
209
200
}
210
- return tail && tail . value ;
201
+ this . size -= 1 ;
202
+ return tail . value ;
211
203
}
212
204
// end::removeLast[]
213
205
214
206
// tag::removeByPosition[]
215
207
/**
216
- * Removes the element at the specified position in this list.
208
+ * Removes the element at the given position (index) in this list.
217
209
* Runtime: O(n)
218
210
* @param {any } position
219
211
* @returns {any } the element's value at the specified position that was removed.
220
212
*/
221
213
removeByPosition ( position = 0 ) {
222
- const current = this . get ( position ) ;
223
-
224
- if ( position === 0 ) {
225
- this . removeFirst ( ) ;
226
- } else if ( position === this . size - 1 ) {
227
- this . removeLast ( ) ;
228
- } else if ( current ) {
229
- current . previous . next = current . next ;
230
- current . next . previous = current . previous ;
231
- this . size -= 1 ;
232
- }
233
-
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 ;
234
221
return current && current . value ;
235
222
}
236
223
// end::removeByPosition[]
237
224
238
- /**
239
- * Removes the first occurrence of the specified elementt
240
- * from this list, if it is present.
241
- * Runtime: O(n)
242
- * @param {any } callbackOrIndex callback or position index to remove
243
- */
244
- remove ( callbackOrIndex ) {
245
- if ( typeof callbackOrIndex !== 'function' ) {
246
- return this . removeByPosition ( parseInt ( callbackOrIndex , 10 ) || 0 ) ;
247
- }
248
-
249
- // find desired position to remove using #find
250
- const position = this . find ( ( node , index ) => {
251
- if ( callbackOrIndex ( node , index ) ) {
252
- return index ;
253
- }
254
- return undefined ;
255
- } ) ;
256
-
257
- if ( position !== undefined ) { // zero-based position.
258
- return this . removeByPosition ( position ) ;
259
- }
260
-
261
- return false ;
262
- }
263
-
264
225
/**
265
226
* Remove element by Node
266
227
* O(1)
@@ -303,6 +264,54 @@ class LinkedList {
303
264
get length ( ) {
304
265
return this . size ;
305
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
+ }
306
315
}
307
316
308
317
// Aliases
0 commit comments