@@ -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