DoublyLinkedListIterator.js
3.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
"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;