1
\$\begingroup\$

I'm looking for feedback on my singly linked list implementation in JavaScript. Please let me know if you have any suggestions on coding style, documentation, bug fixes, etc.

UML / code overview

LinkedList
 _head - The first element in the list
 _size - The number of elements in the list
 getSize()
 Returns the number of elements in the list
 get(index)
 Returns the node at index
 Throws a ReferenceError if index is not specified
 Throws a RangeError if index is out of range
 getFirst()
 Returns the first element in the list
 Calls and returns this.get(0)
 getLast()
 Returns the last element in the list
 Calls and returns this.get(this._size - 1)
 add(node, index)
 Inserts the given node at the specified index
 Throws a ReferenceError if node is not passed
 Throws a TypeError if node is not an instance of ListNode
 addFirst(node)
 Inserts the given node at the front of the list
 Calls and returns add(node, 0)
 addLast(node)
 Appends the given node at the end of the list
 Calls and returns add(node, this._size - 1)
 remove(index)
 Removes and returns the node at the specified index
 Throws a ReferenceError if index is undefined
 Throws a RangeError if index is not between 0 and this._size
 removeFirst()
 Removes and returns the first node in the list
 Calls and returns remove(0)
 removeLast()
 Removes and returns the last node in the list
 Calls and returns remove(0)
 clear()
 Removes all elements in the list
 set(index, node)
 Replaces the node at index with the given node
 Calls this.remove(index) then calls and returns this.add(node, index)
 indexOf(value)
 Returns -1 or the first index value is found at
 Throws a ReferenceError if value is undefined
 lastIndexOf(value)
 Returns -1 or the last index value is found at
 Throws a ReferenceError if value is undefined
 contains(value)
 Returns true/false if the value is/not in the list
ListNode
 _value - The value this node holds
 _next - The next node in the list
 getValue()
 setValue(value)
 getNext()
 setNext(node)

Code

I've left out the ListNode class because there's nothing more to it than the UML above.

class LinkedList {
 // Initializes the list by setting the head to null,
 // and the size to 0
 constructor() {
 this._head = null;
 this._size = 0;
 }
 // Returns the number of nodes in the list
 getSize() {
 return this._size;
 }
 // Returns the head of the list
 getFirst() {
 return this._head;
 }
 // Returns the tail of the list
 getLast() {
 return this.get(this._size - 1);
 }
 // Returns the node at the specified index
 get(index) {
 // If the index parameter was not specified
 if (index === undefined) {
 throw new ReferenceError('no index was specified', 'LinkedList.js');
 }
 // Make sure the index is in range
 if (index < 0 || index >= this._size) {
 const msg = `Index (${index}) out of range; List size (${this._size})`;
 throw new RangeError(msg, 'LinkedList.js');
 }
 let current = this._head;
 for (let i = 0; i < index; i++) {
 current = current.getNext();
 }
 return current;
 }
 // Inserts the given node at the specified index
 add(node, index) {
 // If the node parameter was not specified
 if (node === undefined) {
 throw new ReferenceError('no node was specified', 'LinkedList.js');
 }
 // Make sure node is a ListNode
 if (!(node instanceof ListNode)) {
 const msg = "add(node, index): node must be of type ListNode";
 throw new TypeError(msg, 'LinkedList.js');
 }
 // The index parameter is optional. If not defined, add node to the end
 if (index === undefined) {
 index = this._size;
 }
 // Make sure the index is in range
 if (index < 0 || index > this._size) {
 const msg = `Index (${index}) out of range; List size (${this._size})`;
 throw new RangeError(msg, 'LinkedList.js');
 }
 // If inserting the node at the beginning of the list
 if (index === 0) {
 // Set the node's next to point to the current head
 node.setNext(this._head);
 // Replace the current head with the new node
 this._head = node;
 this._size++;
 return node;
 } else {
 // The node just before the insertion index
 let previous = this.get(index - 1);
 // The node at the insertion index
 let next = previous.getNext();
 // Set the node at the insertion index
 previous.setNext(node);
 node.setNext(next);
 this._size++;
 return node;
 }
 }
 // Inserts the given node at the beginning of the list
 addFirst(node) {
 return this.add(node, 0);
 }
 // Appends the given node at the beginning of the list
 addLast(node) {
 return this.add(node, this._size);
 }
 // Removes and returns the node at the specified index
 remove(index) {
 // If the index parameter was not specified
 if (index === undefined) {
 throw new ReferenceError('no index was specified', 'LinkedList.js');
 }
 // Make sure the index is in range
 if (index < 0 || index > this._size) {
 const msg = `Index (${index}) out of range; List size (${this._size})`;
 throw new RangeError(msg, 'LinkedList.js');
 }
 // If removing the head of the list
 if (index === 0) {
 const removed = this._head;
 this._head = this._head.getNext();
 this._size--;
 return removed;
 } else {
 // The node at the index before the one to be removed
 let previous = this.get(index - 1);
 // The node to be removed
 let removed = previous.getNext();
 // Removes the node at index by setting the next pointer of the node at
 // index-1 to the node at index+1, skipping the node at index
 previous.setNext(removed.getNext());
 this._size--;
 return removed;
 }
 }
 // Removes and returns the head of the list
 removeFirst() {
 return this.remove(0);
 }
 // Removes and returns the last node in the list
 removeLast() {
 return this.remove(this._size - 1);
 }
 // Empties the list by setting the head to null and sets the size to 0
 clear() {
 this._head = null;
 this._size = 0;
 }
 // Replaces the node at the specified index with the given node
 set(index, node) {
 // Remove the node currently at index
 this.remove(index);
 // Inserts the new node at the specified index
 return this.add(node, index);
 }
 // Returns the index that the value is found at, -1 if the value is not found
 indexOf(value) {
 // If the index parameter was not specified
 if (value === undefined) {
 throw new ReferenceError('no value was specified', 'LinkedList.js');
 }
 // If the list is empty, return -1 indicating the value wasn't found
 if (this._head == null) {
 return -1;
 }
 let current = this._head;
 let index = 0;
 while (current.getNext() != null) {
 if (current.getValue() === value) {
 return index;
 }
 current = current.getNext();
 index++;
 }
 return -1;
 }
 // Returns the last index the value is found at, -1 if it is not found
 lastIndexOf(value) {
 // If the index parameter was not specified
 if (value === undefined) {
 throw new ReferenceError('no value was specified', 'LinkedList.js');
 }
 // If the list is empty, return -1 indicating the value wasn't found
 if (this._head == null) {
 return -1;
 }
 let current = this._head;
 let lastMatch = -1;
 let index = 0;
 while (current != null) {
 // If a match is found, update the lastMatch to the current index
 if (current.getValue() === value) {
 lastMatch = index;
 }
 current = current.getNext();
 index++;
 }
 return lastMatch;
 }
 // Returns true if the value is in the list, false otherwise
 contains(value) {
 // Returns true if indexOf() finds the value, false if it does not
 return (this.indexOf(value) !== -1);
 }
}
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Oct 12, 2016 at 3:04
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

You could change your if condition

if (index === undefined) {
 throw RangeError('no index passed')
} else {
 // do the funk
}

by

if (typeof(index) !== "undefined" && index !== null) {
 throw RangeError('no index passed')
} else {
 // do the funk
}

Because in your case you will have an error if you pass some null in your functions.

answered Oct 12, 2016 at 7:48
\$\endgroup\$
2
  • \$\begingroup\$ Would it be better to just do if (index == null) since that will be true if index is undefined and if index is null? \$\endgroup\$ Commented Oct 12, 2016 at 18:17
  • 1
    \$\begingroup\$ @MatthewCliatt try not to use == it is bad practice. It could be hard to debug when your codebase is going to grow ;) \$\endgroup\$ Commented Oct 13, 2016 at 17:08

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.