DoublyLinkedListIterator.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. "use strict";
  2. /**
  3. * Creates an interator for a DoublyLinkedList starting at the given node
  4. * It's internal cursor will remains relative to the last "iterated" node as that
  5. * node moves through the list until it either iterates to the end of the list,
  6. * or the the node it's tracking is removed from the list. Until the first 'next'
  7. * call it tracks the head/tail of the linked list. This means that one can create
  8. * an iterator on an empty list, then add nodes, and then the iterator will follow
  9. * those nodes. Because the DoublyLinkedList nodes don't track their owning "list" and
  10. * it's highly inefficient to walk the list for every iteration, the iterator won't know
  11. * if the node has been detached from one List and added to another list, or if the iterator
  12. *
  13. * The created object is an es6 compatible iterator
  14. */
  15. class DoublyLinkedListIterator {
  16. /**
  17. * @param {Object} doublyLinkedList a node that is part of a doublyLinkedList
  18. * @param {Boolean} [reverse=false] is this a reverse iterator? default: false
  19. */
  20. constructor(doublyLinkedList, reverse) {
  21. this._list = doublyLinkedList;
  22. // NOTE: these key names are tied to the DoublyLinkedListIterator
  23. this._direction = reverse === true ? "prev" : "next";
  24. this._startPosition = reverse === true ? "tail" : "head";
  25. this._started = false;
  26. this._cursor = null;
  27. this._done = false;
  28. }
  29. _start() {
  30. this._cursor = this._list[this._startPosition];
  31. this._started = true;
  32. }
  33. _advanceCursor() {
  34. if (this._started === false) {
  35. this._started = true;
  36. this._cursor = this._list[this._startPosition];
  37. return;
  38. }
  39. this._cursor = this._cursor[this._direction];
  40. }
  41. reset() {
  42. this._done = false;
  43. this._started = false;
  44. this._cursor = null;
  45. }
  46. remove() {
  47. if (
  48. this._started === false ||
  49. this._done === true ||
  50. this._isCursorDetached()
  51. ) {
  52. return false;
  53. }
  54. this._list.remove(this._cursor);
  55. }
  56. next() {
  57. if (this._done === true) {
  58. return { done: true };
  59. }
  60. this._advanceCursor();
  61. // if there is no node at the cursor or the node at the cursor is no longer part of
  62. // a doubly linked list then we are done/finished/kaput
  63. if (this._cursor === null || this._isCursorDetached()) {
  64. this._done = true;
  65. return { done: true };
  66. }
  67. return {
  68. value: this._cursor,
  69. done: false
  70. };
  71. }
  72. /**
  73. * Is the node detached from a list?
  74. * NOTE: you can trick/bypass/confuse this check by removing a node from one DoublyLinkedList
  75. * and adding it to another.
  76. * TODO: We can make this smarter by checking the direction of travel and only checking
  77. * the required next/prev/head/tail rather than all of them
  78. * @return {Boolean} [description]
  79. */
  80. _isCursorDetached() {
  81. return (
  82. this._cursor.prev === null &&
  83. this._cursor.next === null &&
  84. this._list.tail !== this._cursor &&
  85. this._list.head !== this._cursor
  86. );
  87. }
  88. }
  89. module.exports = DoublyLinkedListIterator;