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