@@ -16,13 +16,6 @@ class LinkedList {
1616  } 
1717  // end::constructor[] 
1818
19-   /** 
20-    * Alias for size 
21-    */ 
22-   get  length ( )  { 
23-     return  this . size ; 
24-   } 
25- 
2619  // tag::addFirst[] 
2720  /** 
2821   * Adds element to the begining of the list. Similar to Array.unshift 
@@ -34,9 +27,9 @@ class LinkedList {
3427
3528    newNode . next  =  this . first ; 
3629
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. 
4033      this . last  =  newNode ; 
4134    } 
4235
@@ -59,11 +52,11 @@ class LinkedList {
5952  addLast ( value )  { 
6053    const  newNode  =  new  Node ( value ) ; 
6154
62-     if  ( this . first )  { 
55+     if  ( this . first )  {   // check if first node exists (list not empty) 
6356      newNode . previous  =  this . last ; 
6457      this . last . next  =  newNode ; 
6558      this . last  =  newNode ; 
66-     }  else  { 
59+     }  else  {   // if list is empty, first & last will point to newNode. 
6760      this . first  =  newNode ; 
6861      this . last  =  newNode ; 
6962    } 
@@ -78,195 +71,157 @@ class LinkedList {
7871  /** 
7972   * Insert new element at the given position (index) 
8073   * 
74+    * Runtime: O(n) 
75+    * 
8176   * @param  {any } value new node's value 
8277   * @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. 
8479   */ 
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> 
8983
90-     if  ( position  ===  this . size )  {  // <2> 
91-       return  this . addLast ( value ) ; 
92-     } 
9384    // 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 ; 
10795  } 
10896  // end::addMiddle[] 
10997
11098  // tag::searchByValue[] 
11199  /** 
100+    * @deprecated  use findBy 
112101   * 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+    * 
114105   * Runtime: O(n) 
106+    * 
115107   * @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 
118110   * @param  {any } value 
119111   * @returns  {number } return index or undefined 
120112   */ 
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 ; 
128115  } 
129116  // end::searchByValue[] 
130117
131118  // tag::searchByIndex[] 
132119  /** 
120+    * @deprecated  use findBy directly 
133121   * Search by index 
134122   * Runtime: O(n) 
135123   * @example : assuming a linked list with: a -> b -> c 
136124   *  linkedList.get(1) // ↪️ 'b' 
137125   *  linkedList.get(40) // ↪️ undefined 
138126   * @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. 
140129   */ 
141130  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 ; 
148132  } 
149133  // end::searchByIndex[] 
150134
151135  // tag::find[] 
152136  /** 
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} } 
158148   */ 
159-   find ( callback )  { 
149+   findBy ( {  value ,  index  =   Infinity   }   =   { } )  { 
160150    for  ( let  current  =  this . first ,  position  =  0 ;  // <1> 
161-       current ;  // <2> 
151+       current   &&   position   <=   index ;  // <2> 
162152      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> 
167155      } 
168156    } 
169-     return  undefined ;  // not found 
157+     return  { } ;  // not found 
170158  } 
171159  // end::find[] 
172160
173161
174162  // tag::removeFirst[] 
175163  /** 
176164   * Removes element from the start of the list (head/root). 
177-    * Similar to Array.shift 
165+    * Similar to Array.shift().  
178166   * Runtime: O(1) 
179167   * @returns  {any } the first element's value which was removed. 
180168   */ 
181169  removeFirst ( )  { 
170+     if  ( ! this . first )  return  null ;  // Check if list is already empty. 
182171    const  head  =  this . first ; 
183172
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 ; 
192178    } 
193-     return  head  &&  head . value ; 
179+     this . size  -=  1 ; 
180+     return  head . value ; 
194181  } 
195182  // end::removeFirst[] 
196183
197184  // tag::removeLast[] 
198185  /** 
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().  
201188   * 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 
203190   */ 
204191  removeLast ( )  { 
192+     if  ( ! this . last )  return  null ;  // Check if list is already empty. 
205193    const  tail  =  this . last ; 
206194
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 ; 
215200    } 
216-     return  tail  &&  tail . value ; 
201+     this . size  -=  1 ; 
202+     return  tail . value ; 
217203  } 
218204  // end::removeLast[] 
219205
220206  // tag::removeByPosition[] 
221207  /** 
222-    * Removes the element at the specified  position in this list. 
208+    * Removes the element at the given  position (index)  in this list. 
223209   * Runtime: O(n) 
224210   * @param  {any } position 
225211   * @returns  {any } the element's value at the specified position that was removed. 
226212   */ 
227213  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 ; 
240221    return  current  &&  current . value ; 
241222  } 
242223  // end::removeByPosition[] 
243224
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- 
270225  /** 
271226   * Remove element by Node 
272227   * O(1) 
@@ -302,6 +257,61 @@ class LinkedList {
302257    const  parts  =  [ ...this ] ;  // see [Symbol.iterator]() 
303258    return  parts . map ( ( n )  =>  util . inspect ( n . node . value ) ) . join ( ' -> ' ) ; 
304259  } 
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+   } 
305315} 
306316
307317// Aliases 
0 commit comments