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);
  }

}
\$\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.

\$\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\$
    – Matt C
    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

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.