Source: collections/LinkedList.js

  1. const { Collection, CollectionEvent } = require('./Collection')
  2. , { EqualityComparer } = require('./EqualityComparer')
  3. , { Observable, fromEvent } = require('rxjs')
  4. , symbolLinkedListAdd = Symbol('linkedListAdd')
  5. , symbolLinkedListRemove = Symbol('linkedListRemove');
  6. /**
  7. * @template T
  8. */
  9. class LinkedListEvent extends CollectionEvent {
  10. /**
  11. * @param {LinkedListNode<T>} node
  12. * @param {Boolean} add True, if the node was added. If false, the node was removed.
  13. * @param {Boolean} first True, iff the node is the first node now
  14. * @param {Boolean} last True, iff the node is the last node now
  15. */
  16. constructor(node, add, first, last) {
  17. super(node);
  18. /** @type {LinkedListNode<T>} */
  19. this.item = node;
  20. /** @protected */
  21. this._add = add;
  22. /** @protected */
  23. this._first = first;
  24. /** @protected */
  25. this._last = last;
  26. };
  27. /**
  28. * Returns whether this represents an add-event.
  29. *
  30. * @type {Boolean}
  31. */
  32. get added() {
  33. return this._add;
  34. };
  35. /**
  36. * Returns whether this represents a remove-event.
  37. *
  38. * @type {Boolean}
  39. */
  40. get removed() {
  41. return !this._add;
  42. };
  43. /**
  44. * Returns whether the node is the first now.
  45. *
  46. * @type {Boolean}
  47. */
  48. get firstNode() {
  49. return this._first;
  50. };
  51. /**
  52. * Returns whether the node is the last now.
  53. *
  54. * @type {Boolean}
  55. */
  56. get lastNode() {
  57. return this._last;
  58. };
  59. };
  60. /**
  61. * @template T
  62. * @author Sebastian Hönel <development@hoenel.net>
  63. */
  64. class LinkedListNode {
  65. /**
  66. * @param {T} item
  67. * @param {LinkedList<T>} list
  68. * @param {LinkedListNode<T>} prev
  69. * @param {LinkedListNode<T>} next
  70. */
  71. constructor(item, list, prev = null, next = null) {
  72. this.item = item;
  73. this.list = list;
  74. this.prev = prev;
  75. this.next = next;
  76. };
  77. };
  78. /**
  79. * @template T
  80. * @author Sebastian Hönel <development@hoenel.net>
  81. */
  82. class LinkedList extends Collection {
  83. /**
  84. * Creates a new, empty doubly-linked LinkedList.
  85. *
  86. * @param {EqualityComparer<T>} [eqComparer] Optional. Defaults To EqualityComparer<T>.default.
  87. */
  88. constructor(eqComparer = EqualityComparer.default) {
  89. super(eqComparer);
  90. /**
  91. * @type {LinkedListNode<T>}
  92. * @protected
  93. */
  94. this._firstNode = null;
  95. /**
  96. * @type {LinkedListNode<T>}
  97. * @protected
  98. */
  99. this._lastNode = null;
  100. /**
  101. * @type {Set<LinkedListNode<T>>}
  102. * @protected
  103. */
  104. this._lookupSet = new Set();
  105. /** @protected */
  106. this._size = 0;
  107. /** @type {Observable<LinkedListEvent<T>>} */
  108. this.observableNodeAdd = Object.freeze(fromEvent(this, symbolLinkedListAdd));
  109. /** @type {Observable<LinkedListEvent<T>>} */
  110. this.observableNodeRemove = Object.freeze(fromEvent(this, symbolLinkedListRemove));
  111. };
  112. /**
  113. * @protected
  114. * @throws {Error} If this LinkedList is empty.
  115. * @returns {Boolean} true
  116. */
  117. _requireNotEmpty() {
  118. if (this._firstNode === null) {
  119. throw new Error('The LinkedList is empty.');
  120. }
  121. return true;
  122. };
  123. /**
  124. * @protected
  125. * @throws {Error} If the given node is not a LinkedListNode.
  126. * @param {LinkedListNode<T>} node
  127. */
  128. _requireIsNode(node) {
  129. if (!(node instanceof LinkedListNode)) {
  130. throw new Error('The node is not a LinkedListNode.');
  131. }
  132. return true;
  133. };
  134. /**
  135. * @protected
  136. * @param {LinkedListNode<T>} node
  137. * @param {T} item
  138. * @param {Boolean} after
  139. * @returns {LinkedListNode<T>} The newly added node.
  140. */
  141. _add(node, item, after = true) {
  142. if (!this.hasNode(node)) {
  143. throw new Error('The node given is not a member of this LinkedList.');
  144. }
  145. const newNode = new LinkedListNode(
  146. item, this, after ? node : node.prev, after ? node.next : node);
  147. if (newNode.prev instanceof LinkedListNode) {
  148. newNode.prev.next = newNode;
  149. if (newNode.prev.prev === null) {
  150. this._firstNode = newNode.prev;
  151. }
  152. } else {
  153. this._firstNode = newNode;
  154. }
  155. if (newNode.next instanceof LinkedListNode) {
  156. newNode.next.prev = newNode;
  157. if (newNode.next.next === null) {
  158. this._lastNode = newNode.next;
  159. }
  160. } else {
  161. this._lastNode = newNode;
  162. }
  163. this._lookupSet.add(newNode);
  164. this._size++;
  165. this.emit(symbolLinkedListAdd, new LinkedListEvent(
  166. newNode, true, this.first === newNode, this.last === newNode));
  167. return newNode;
  168. };
  169. /**
  170. * @override
  171. * @inheritDoc
  172. * @type {Number}
  173. */
  174. get size() {
  175. return this._size;
  176. };
  177. /**
  178. * @override
  179. * @inheritDoc
  180. * @type {Boolean}
  181. */
  182. get isEmpty() {
  183. return this._size === 0;
  184. };
  185. /**
  186. * @type {LinkedListNode<T>}
  187. */
  188. get first() {
  189. this._requireNotEmpty();
  190. return this._firstNode;
  191. };
  192. /**
  193. * @type {LinkedListNode<T>}
  194. */
  195. get last() {
  196. this._requireNotEmpty();
  197. return this._lastNode;
  198. };
  199. /**
  200. * @returns {IterableIterator<LinkedListNode<T>>}
  201. */
  202. *nodes() {
  203. let node = this._firstNode;
  204. while (node instanceof LinkedListNode) {
  205. yield node;
  206. node = node.next;
  207. }
  208. };
  209. /**
  210. * @returns {IterableIterator<LinkedListNode<T>>}
  211. */
  212. *nodesReversed() {
  213. let node = this._lastNode;
  214. while (node instanceof LinkedListNode) {
  215. yield node;
  216. node = node.prev;
  217. }
  218. };
  219. /**
  220. * @returns {IterableIterator<T>}
  221. */
  222. *entries() {
  223. for (const node of this.nodes()) {
  224. yield node.item;
  225. }
  226. };
  227. /**
  228. * @returns {IterableIterator<T>}
  229. */
  230. *entriesReversed() {
  231. for (const node of this.nodesReversed()) {
  232. yield node.item;
  233. }
  234. };
  235. /**
  236. * @override
  237. * @inheritDoc
  238. * @param {T} item
  239. * @param {EqualityComparer<T>} [eqComparer] an optional EqualityComparer to use. If not provided, will use the Collection's EqualityComparer.
  240. * @returns {Boolean}
  241. */
  242. has(item, eqComparer = null) {
  243. eqComparer = eqComparer instanceof EqualityComparer ? eqComparer : this._eqComparer;
  244. for (const value of this.entries()) {
  245. if (eqComparer.equals(item, value)) {
  246. return true;
  247. }
  248. }
  249. return false;
  250. };
  251. /**
  252. * Returns a value indicating whether or not this LinkedList has
  253. * the node provided.
  254. *
  255. * @param {LinkedListNode<T>} node
  256. * @returns {Boolean}
  257. */
  258. hasNode(node) {
  259. this._requireIsNode(node);
  260. return this._lookupSet.has(node);
  261. };
  262. /**
  263. * @override
  264. * @inheritDoc
  265. * @returns {this}
  266. */
  267. clear() {
  268. super.clear();
  269. this._firstNode = null;
  270. this._lastNode = null;
  271. this._size = 0;
  272. this._lookupSet.clear();
  273. return this;
  274. };
  275. /**
  276. * @param {T} item
  277. * @returns {LinkedListNode<T>} The node that has been created.
  278. */
  279. addFirst(item) {
  280. if (this.isEmpty) {
  281. this._size++;
  282. this._firstNode = this._lastNode = new LinkedListNode(item, this);
  283. this._lookupSet.add(this._firstNode);
  284. this.emit(symbolLinkedListAdd, new LinkedListEvent(
  285. this._firstNode, true, true, true));
  286. return this._firstNode;
  287. } else {
  288. return this._add(this.first, item, false);
  289. }
  290. };
  291. /**
  292. * @param {T} item
  293. * @returns {LinkedListNode<T>} The node that has been created.
  294. */
  295. addLast(item) {
  296. if (this.isEmpty) {
  297. this._size++;
  298. this._lastNode = this._firstNode = new LinkedListNode(item, this);
  299. this._lookupSet.add(this._lastNode);
  300. this.emit(symbolLinkedListAdd, new LinkedListEvent(
  301. this._lastNode, true, true, true));
  302. return this._lastNode;
  303. } else {
  304. return this._add(this.last, item, true);
  305. }
  306. };
  307. /**
  308. * @param {LinkedListNode<T>} node
  309. * @param {T} item
  310. * @returns {LinkedListNode<T>} The newly created and inserted node.
  311. */
  312. addAfter(node, item) {
  313. return this._add(node, item, true);
  314. };
  315. /**
  316. * @param {LinkedListNode<T>} node
  317. * @param {T} item
  318. */
  319. addBefore(node, item) {
  320. return this._add(node, item, false);
  321. };
  322. /**
  323. * @param {LinkedListNode<T>} node
  324. * @returns {LinkedListNode} The removed node
  325. */
  326. remove(node) {
  327. if (!this.hasNode(node)) {
  328. throw new Error('This LinkedList does not have the given node.');
  329. }
  330. /**
  331. * @param {LinkedListNode<T>} theNode
  332. */
  333. const checkHeadTail = theNode => {
  334. if (theNode.prev === null) {
  335. this._firstNode = theNode;
  336. }
  337. if (theNode.next === null) {
  338. this._lastNode = theNode;
  339. };
  340. }
  341. let wasFirst = true, wasLast = true;
  342. if (node.prev instanceof LinkedListNode) {
  343. node.prev.next = node.next instanceof LinkedListNode ? node.next : null;
  344. checkHeadTail(node.prev);
  345. wasFirst = false;
  346. }
  347. if (node.next instanceof LinkedListNode) {
  348. node.next.prev = node.prev instanceof LinkedListNode ? node.prev : null;
  349. checkHeadTail(node.next);
  350. wasLast = false;
  351. }
  352. node.next = node.prev = null;
  353. this._size--;
  354. this._lookupSet.delete(node);
  355. this.emit(symbolLinkedListRemove, new LinkedListEvent(
  356. node, false, wasFirst, wasLast));
  357. return node;
  358. };
  359. /**
  360. * @returns {LinkedListNode<T>} The first node after it has been removed
  361. */
  362. removeFirst() {
  363. return this.remove(this.first);
  364. };
  365. /**
  366. * @returns {LinkedListNode<T>} The last node after it has been removed.
  367. */
  368. removeLast() {
  369. return this.remove(this.last);
  370. };
  371. };
  372. module.exports = Object.freeze({
  373. LinkedList,
  374. LinkedListNode,
  375. LinkedListEvent,
  376. symbolLinkedListAdd,
  377. symbolLinkedListRemove
  378. });