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