const head = Symbol("head"); const tail = Symbol("tail"); class CircularDoublyLinkedListNode { constructor (data) { this.data = data; this.next = null; this.previous = null; } } export class CircularDoublyLinkedList { constructor () { this[head] = null; this[tail] = null; } add (data) { const newNode = new CircularDoublyLinkedListNode(data); // Special case: no items in the list if (this[head] === null) { this[head] = newNode; newNode.next = newNode; newNode.previous = newNode; } else { const tail = this[head].previous; tail.next = newNode; newNode.previous = tail; newNode.next = this[head]; this[head].previous = newNode; } } get (index) { if ( ! Number(index) < 0) { return undefined; } let current = this[head]; let i = 0; do { if (i === index) { return current.data; } current = current.next; i++; } while ((current !== this[head]) && (i <= index)); return undefined; } remove (index) { if ((this[head] === null) || (index < 0)) { throw new RangeError(`Index ${index} does not exist in the list.`); } let current = this[head]; // Removing the first node if (index === 0) { if (current.next === this[head]) { this[head] = null; } else { const tail = this[head].previous; // Set the tail to point to the second item in the list // and point the new item to the tail tail.next = current.next; current.next.previous = tail; this[head] = tail.next; } return current.data; } let i = 0; do { current = current.next; i++; } while ((current !== this[head]) && (i < index)); if (current !== this[head]) { // skip over the node to remove it current.previous.next = current.next; current.next.previous = current.previous; return current.data; } throw new RangeError(`Index ${index} does not exist in the list.`); } *values () { // list is empty if (this[head] !== null) { // only one node if (this[head].next === this[head]) { yield this[head].data; } else { let current = this[head]; do { yield current.data; current = current.next; } while (current !== this[head]); } } } *circularValues () { if (this[head] !== null) { let current = this[head]; // infinite loop while (true) { yield current.data; current = current.next; } } } [Symbol.iterator] () { return this.values(); } }