@@ -27,9 +27,9 @@ class LinkedList {
2727
2828    newNode . next  =  this . first ; 
2929
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. 
3333      this . last  =  newNode ; 
3434    } 
3535
@@ -52,11 +52,11 @@ class LinkedList {
5252  addLast ( value )  { 
5353    const  newNode  =  new  Node ( value ) ; 
5454
55-     if  ( this . first )  { 
55+     if  ( this . first )  {   // check if first node exists (list not empty) 
5656      newNode . previous  =  this . last ; 
5757      this . last . next  =  newNode ; 
5858      this . last  =  newNode ; 
59-     }  else  { 
59+     }  else  {   // if list is empty, first & last will point to newNode. 
6060      this . first  =  newNode ; 
6161      this . last  =  newNode ; 
6262    } 
@@ -71,20 +71,18 @@ class LinkedList {
7171  /** 
7272   * Insert new element at the given position (index) 
7373   * 
74+    * Runtime: O(n) 
75+    * 
7476   * @param  {any } value new node's value 
7577   * @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. 
7779   */ 
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> 
8283
83-     if  ( position  ===  this . size )  {  // <2> 
84-       return  this . addLast ( value ) ; 
85-     } 
8684    // Adding element in the middle 
87-     const  current  =  this . get ( position ) ; 
85+     const  current  =  this . findBy ( {   index :  position   } ) . node ; 
8886    if  ( ! current )  return  undefined ;  // out of bound index 
8987
9088    const  newNode  =  new  Node ( value ) ;  // <3> 
@@ -99,6 +97,7 @@ class LinkedList {
9997
10098  // tag::searchByValue[] 
10199  /** 
100+    * @deprecated  use findBy 
102101   * Search by value. It finds first occurrence  of 
103102   * the position of element matching the value. 
104103   * Similar to Array.indexOf. 
@@ -112,17 +111,13 @@ class LinkedList {
112111   * @returns  {number } return index or undefined 
113112   */ 
114113  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 ; 
121115  } 
122116  // end::searchByValue[] 
123117
124118  // tag::searchByIndex[] 
125119  /** 
120+    * @deprecated  use findBy directly 
126121   * Search by index 
127122   * Runtime: O(n) 
128123   * @example : assuming a linked list with: a -> b -> c 
@@ -133,134 +128,100 @@ class LinkedList {
133128   *   this list or undefined if was not found. 
134129   */ 
135130  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 ; 
142132  } 
143133  // end::searchByIndex[] 
144134
145135  // tag::find[] 
146136  /** 
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} } 
152148   */ 
153-   find ( callback )  { 
149+   findBy ( {  value ,  index  =   Infinity   }   =   { } )  { 
154150    for  ( let  current  =  this . first ,  position  =  0 ;  // <1> 
155-       current ;  // <2> 
151+       current   &&   position   <=   index ;  // <2> 
156152      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> 
161155      } 
162156    } 
163-     return  undefined ;  // not found 
157+     return  { } ;  // not found 
164158  } 
165159  // end::find[] 
166160
167161
168162  // tag::removeFirst[] 
169163  /** 
170164   * Removes element from the start of the list (head/root). 
171-    * Similar to Array.shift 
165+    * Similar to Array.shift().  
172166   * Runtime: O(1) 
173167   * @returns  {any } the first element's value which was removed. 
174168   */ 
175169  removeFirst ( )  { 
170+     if  ( ! this . first )  return  null ;  // Check if list is already empty. 
176171    const  head  =  this . first ; 
177172
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 ; 
186178    } 
187-     return  head  &&  head . value ; 
179+     this . size  -=  1 ; 
180+     return  head . value ; 
188181  } 
189182  // end::removeFirst[] 
190183
191184  // tag::removeLast[] 
192185  /** 
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().  
195188   * 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 
197190   */ 
198191  removeLast ( )  { 
192+     if  ( ! this . last )  return  null ;  // Check if list is already empty. 
199193    const  tail  =  this . last ; 
200194
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 ; 
209200    } 
210-     return  tail  &&  tail . value ; 
201+     this . size  -=  1 ; 
202+     return  tail . value ; 
211203  } 
212204  // end::removeLast[] 
213205
214206  // tag::removeByPosition[] 
215207  /** 
216-    * Removes the element at the specified  position in this list. 
208+    * Removes the element at the given  position (index)  in this list. 
217209   * Runtime: O(n) 
218210   * @param  {any } position 
219211   * @returns  {any } the element's value at the specified position that was removed. 
220212   */ 
221213  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 ; 
234221    return  current  &&  current . value ; 
235222  } 
236223  // end::removeByPosition[] 
237224
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- 
264225  /** 
265226   * Remove element by Node 
266227   * O(1) 
@@ -303,6 +264,54 @@ class LinkedList {
303264  get  length ( )  { 
304265    return  this . size ; 
305266  } 
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+   } 
306315} 
307316
308317// Aliases 
0 commit comments