You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
100 lines
3.0 KiB
100 lines
3.0 KiB
"use strict";
|
|
|
|
/**
|
|
* Creates an interator for a DoublyLinkedList starting at the given node
|
|
* It's internal cursor will remains relative to the last "iterated" node as that
|
|
* node moves through the list until it either iterates to the end of the list,
|
|
* or the the node it's tracking is removed from the list. Until the first 'next'
|
|
* call it tracks the head/tail of the linked list. This means that one can create
|
|
* an iterator on an empty list, then add nodes, and then the iterator will follow
|
|
* those nodes. Because the DoublyLinkedList nodes don't track their owning "list" and
|
|
* it's highly inefficient to walk the list for every iteration, the iterator won't know
|
|
* if the node has been detached from one List and added to another list, or if the iterator
|
|
*
|
|
* The created object is an es6 compatible iterator
|
|
*/
|
|
class DoublyLinkedListIterator {
|
|
/**
|
|
* @param {Object} doublyLinkedList a node that is part of a doublyLinkedList
|
|
* @param {Boolean} [reverse=false] is this a reverse iterator? default: false
|
|
*/
|
|
constructor(doublyLinkedList, reverse) {
|
|
this._list = doublyLinkedList;
|
|
// NOTE: these key names are tied to the DoublyLinkedListIterator
|
|
this._direction = reverse === true ? "prev" : "next";
|
|
this._startPosition = reverse === true ? "tail" : "head";
|
|
this._started = false;
|
|
this._cursor = null;
|
|
this._done = false;
|
|
}
|
|
|
|
_start() {
|
|
this._cursor = this._list[this._startPosition];
|
|
this._started = true;
|
|
}
|
|
|
|
_advanceCursor() {
|
|
if (this._started === false) {
|
|
this._started = true;
|
|
this._cursor = this._list[this._startPosition];
|
|
return;
|
|
}
|
|
this._cursor = this._cursor[this._direction];
|
|
}
|
|
|
|
reset() {
|
|
this._done = false;
|
|
this._started = false;
|
|
this._cursor = null;
|
|
}
|
|
|
|
remove() {
|
|
if (
|
|
this._started === false ||
|
|
this._done === true ||
|
|
this._isCursorDetached()
|
|
) {
|
|
return false;
|
|
}
|
|
this._list.remove(this._cursor);
|
|
}
|
|
|
|
next() {
|
|
if (this._done === true) {
|
|
return { done: true };
|
|
}
|
|
|
|
this._advanceCursor();
|
|
|
|
// if there is no node at the cursor or the node at the cursor is no longer part of
|
|
// a doubly linked list then we are done/finished/kaput
|
|
if (this._cursor === null || this._isCursorDetached()) {
|
|
this._done = true;
|
|
return { done: true };
|
|
}
|
|
|
|
return {
|
|
value: this._cursor,
|
|
done: false
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Is the node detached from a list?
|
|
* NOTE: you can trick/bypass/confuse this check by removing a node from one DoublyLinkedList
|
|
* and adding it to another.
|
|
* TODO: We can make this smarter by checking the direction of travel and only checking
|
|
* the required next/prev/head/tail rather than all of them
|
|
* @return {Boolean} [description]
|
|
*/
|
|
_isCursorDetached() {
|
|
return (
|
|
this._cursor.prev === null &&
|
|
this._cursor.next === null &&
|
|
this._list.tail !== this._cursor &&
|
|
this._list.head !== this._cursor
|
|
);
|
|
}
|
|
}
|
|
|
|
module.exports = DoublyLinkedListIterator;
|
|
|