collect-your-stuff 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +802 -77
  2. package/dist/collections/arrayable/ArrayElement.d.ts +2 -0
  3. package/dist/collections/arrayable/ArrayElement.js +4 -2
  4. package/dist/collections/arrayable/ArrayElement.min.js +1 -1
  5. package/dist/collections/arrayable/Arrayable.d.ts +27 -13
  6. package/dist/collections/arrayable/Arrayable.js +47 -12
  7. package/dist/collections/arrayable/Arrayable.min.js +1 -1
  8. package/dist/collections/doubly-linked-list/DoubleLinker.d.ts +5 -1
  9. package/dist/collections/doubly-linked-list/DoubleLinker.js +5 -1
  10. package/dist/collections/doubly-linked-list/DoublyLinkedList.d.ts +25 -13
  11. package/dist/collections/doubly-linked-list/DoublyLinkedList.js +106 -53
  12. package/dist/collections/doubly-linked-list/DoublyLinkedList.min.js +1 -1
  13. package/dist/collections/linked-list/LinkedList.d.ts +27 -10
  14. package/dist/collections/linked-list/LinkedList.js +106 -43
  15. package/dist/collections/linked-list/LinkedList.min.js +1 -1
  16. package/dist/collections/linked-list/Linker.d.ts +5 -2
  17. package/dist/collections/linked-list/Linker.js +9 -5
  18. package/dist/collections/linked-list/Linker.min.js +1 -1
  19. package/dist/collections/linked-tree-list/LinkedTreeList.d.ts +16 -4
  20. package/dist/collections/linked-tree-list/LinkedTreeList.js +25 -22
  21. package/dist/collections/linked-tree-list/LinkedTreeList.min.js +1 -1
  22. package/dist/collections/linked-tree-list/TreeLinker.d.ts +7 -1
  23. package/dist/collections/linked-tree-list/TreeLinker.js +7 -1
  24. package/dist/collections/queue/Queue.d.ts +6 -3
  25. package/dist/collections/queue/Queue.js +23 -18
  26. package/dist/collections/queue/Queue.min.js +1 -1
  27. package/dist/collections/queue/Queueable.d.ts +10 -4
  28. package/dist/collections/queue/Queueable.js +11 -6
  29. package/dist/collections/queue/Queueable.min.js +1 -1
  30. package/dist/collections/stack/Stack.d.ts +4 -3
  31. package/dist/collections/stack/Stack.js +4 -4
  32. package/dist/collections/stack/Stack.min.js +1 -1
  33. package/dist/collections/stack/Stackable.d.ts +4 -1
  34. package/dist/collections/stack/Stackable.js +7 -5
  35. package/dist/collections/stack/Stackable.min.js +1 -1
  36. package/dist/main.d.ts +2 -2
  37. package/dist/recipes/ArrayIterator.d.ts +10 -0
  38. package/dist/recipes/ArrayIterator.js +10 -0
  39. package/dist/recipes/DoubleLinkerIterator.d.ts +9 -0
  40. package/dist/recipes/DoubleLinkerIterator.js +9 -0
  41. package/dist/recipes/LinkerIterator.d.ts +9 -0
  42. package/dist/recipes/LinkerIterator.js +9 -0
  43. package/dist/recipes/Runnable.d.ts +3 -2
  44. package/dist/recipes/Runnable.js +3 -2
  45. package/dist/recipes/TreeLinkerIterator.d.ts +9 -0
  46. package/dist/recipes/TreeLinkerIterator.js +9 -0
  47. package/package.json +2 -1
@@ -23,11 +23,19 @@ var _LinkedList = require('../linked-list/LinkedList')
23
23
  class DoublyLinkedList {
24
24
  /**
25
25
  * Create the new DoublyLinkedList instance.
26
+ * @param {DoubleLinker} [linkerClass=DoubleLinker] The class used to wrap given data as linkers.
26
27
  */
27
28
  constructor (linkerClass = _DoubleLinker.DoubleLinker) {
29
+ /** The class used to create this instance, so that it can be recognized as valid without an instanceof check. */
28
30
  this.classType = DoublyLinkedList
31
+ /** A linker of the list (null when the list is empty); the head is found by walking back from it. */
29
32
  this.innerList = null
33
+ /** Whether the inner list has been initialized (it can only be initialized once). */
30
34
  this.initialized = false
35
+ /** The last linker, remembered so that adding to the end does not need to walk the whole list (null when not known yet). */
36
+ this.tailCache = null
37
+ /** The number of linkers, kept up to date by the list's own methods so that the length does not need to walk the whole list (null when not known yet). */
38
+ this.countCache = null
31
39
  this.linkerClass = linkerClass
32
40
  }
33
41
 
@@ -37,11 +45,12 @@ class DoublyLinkedList {
37
45
  * @return {DoublyLinkedList}
38
46
  */
39
47
  initialize (initialList) {
48
+ // Borrowed from LinkedList, which types its return as a LinkedList although it returns whatever list called it
40
49
  return _LinkedList.LinkedList.prototype.initialize.call(this, initialList)
41
50
  }
42
51
 
43
52
  /**
44
- * Retrieve a copy of the innerList used.
53
+ * Retrieve the innerList used (the list itself, not a copy).
45
54
  * @returns {DoubleLinker}
46
55
  */
47
56
  get list () {
@@ -53,91 +62,122 @@ class DoublyLinkedList {
53
62
  * @returns {DoubleLinker}
54
63
  */
55
64
  get first () {
56
- return this.reset()
65
+ let head = this.innerList
66
+ if (head === null) {
67
+ return null
68
+ }
69
+ // innerList is normally the head already, walking back also finds anything linked on before it outside of this list
70
+ while (head.prev !== null) {
71
+ head = head.prev
72
+ }
73
+ this.innerList = head
74
+ return head
57
75
  }
58
76
 
59
77
  /**
60
- * Retrieve the last DoubleLinker in the list.
78
+ * Retrieve the last DoubleLinker in the list. The end is remembered, so this does not walk the list.
61
79
  * @returns {DoubleLinker}
62
80
  */
63
81
  get last () {
64
- let tail = this.innerList
65
- if (tail === null) {
82
+ if (this.innerList === null) {
66
83
  return null
67
84
  }
68
- let next = tail.next
69
- while (next !== null) {
70
- tail = next
71
- next = tail.next
85
+ let tail = this.tailCache !== null ? this.tailCache : this.innerList
86
+ // The remembered tail is normally the end already, walking on from it also finds anything linked on outside of this list
87
+ while (tail.next !== null) {
88
+ tail = tail.next
72
89
  }
90
+ this.tailCache = tail
73
91
  return tail
74
92
  }
75
93
 
76
94
  /**
77
- * Return the length of the list.
95
+ * Return the length of the list. It is kept up to date by the list's own methods, so this does not walk the list
96
+ * (call reset() after linkers were changed directly).
78
97
  * @returns {number}
79
98
  */
80
99
  get length () {
81
- let current = this.first
82
- let length = 0
83
- while (current !== null) {
84
- ++length
85
- current = current.next
100
+ if (this.countCache === null) {
101
+ this.reset()
86
102
  }
87
- return length
103
+ return this.countCache
88
104
  }
89
105
 
90
106
  /**
91
107
  * Insert a new node (or data) after a node.
92
- * @param {DoubleLinker|*} node The existing node as reference
108
+ * @param {DoubleLinker|*} node The existing node as reference (which must be in this list, this is not checked), or null to insert at the start of the list
93
109
  * @param {DoubleLinker|*} newNode The new node to go after the existing node
94
110
  * @returns {DoublyLinkedList}
95
111
  */
96
112
  insertAfter (node, newNode) {
97
- newNode = this.linkerClass.make(newNode)
98
- if (node !== null) {
113
+ newNode = this.linkerClass.make(newNode, this.linkerClass)
114
+ if (node === null || typeof node === 'undefined') {
115
+ // After nothing means at the start of the list
116
+ const head = this.first
117
+ newNode.prev = null
118
+ newNode.next = head
119
+ if (head) {
120
+ head.prev = newNode
121
+ } else {
122
+ this.tailCache = newNode
123
+ }
124
+ this.innerList = newNode
125
+ } else {
99
126
  // Ensure the next reference of this node is assigned to the new node
100
127
  newNode.next = node.next
101
128
  // Ensure this node is assigned as the prev reference of the new node
102
129
  newNode.prev = node
103
130
  // Then set this node's next reference to the new node
104
131
  node.next = newNode
132
+ if (newNode.next) {
133
+ // Update the next reference to ensure circular reference for prev points to the new node
134
+ newNode.next.prev = newNode
135
+ } else {
136
+ this.tailCache = newNode
137
+ }
105
138
  }
106
- if (newNode.next) {
107
- // Update the next reference to ensure circular reference for prev points to the new node
108
- newNode.next.prev = newNode
139
+ if (this.countCache !== null) {
140
+ ++this.countCache
109
141
  }
110
- if (!this.length) {
111
- this.innerList = newNode
112
- }
113
- this.reset()
114
142
  return this
115
143
  }
116
144
 
117
145
  /**
118
146
  * Insert a new node (or data) before a node.
119
- * @param {DoubleLinker|*} node The existing node as reference
147
+ * @param {DoubleLinker|*} node The existing node as reference (which must be in this list, this is not checked), or null to insert at the end of the list
120
148
  * @param {DoubleLinker|*} newNode The new node to go before the existing node
121
149
  * @returns {DoublyLinkedList}
122
150
  */
123
151
  insertBefore (node, newNode) {
124
- newNode = this.linkerClass.make(newNode)
125
- if (node !== null) {
152
+ newNode = this.linkerClass.make(newNode, this.linkerClass)
153
+ if (node === null || typeof node === 'undefined') {
154
+ // Before nothing means at the end of the list
155
+ const tail = this.last
156
+ newNode.next = null
157
+ newNode.prev = tail
158
+ if (tail === null) {
159
+ this.innerList = newNode
160
+ } else {
161
+ tail.next = newNode
162
+ }
163
+ this.tailCache = newNode
164
+ } else {
126
165
  // The new node will reference this prev node as prev
127
166
  newNode.prev = node.prev
128
167
  // The new node will reference this node as next
129
168
  newNode.next = node
130
169
  // This prev will reference the new node
131
170
  node.prev = newNode
171
+ if (newNode.prev) {
172
+ // Update the prev reference to ensure circular reference for next points to the new node
173
+ newNode.prev.next = newNode
174
+ } else {
175
+ this.innerList = newNode
176
+ }
132
177
  }
133
- if (newNode.prev) {
134
- // Update the prev reference to ensure circular reference for next points to the new node
135
- newNode.prev.next = newNode
136
- }
137
- if (!this.length) {
138
- this.innerList = newNode
178
+ if (this.countCache !== null) {
179
+ ++this.countCache
139
180
  }
140
- this.reset()
141
181
  return this
142
182
  }
143
183
 
@@ -167,7 +207,7 @@ class DoublyLinkedList {
167
207
  * @return {DoubleLinker}
168
208
  */
169
209
  remove (node) {
170
- if (node === null) {
210
+ if (node === null || typeof node === 'undefined') {
171
211
  return null
172
212
  }
173
213
  if (node.prev) {
@@ -183,35 +223,47 @@ class DoublyLinkedList {
183
223
  if (this.innerList === node) {
184
224
  this.innerList = node.next || node.prev || null
185
225
  }
186
- // Update head reference
187
- this.reset()
226
+ if (this.tailCache === node) {
227
+ this.tailCache = node.prev
228
+ }
229
+ if (this.innerList === null) {
230
+ this.tailCache = null
231
+ }
232
+ if (this.countCache !== null) {
233
+ --this.countCache
234
+ }
188
235
  return node
189
236
  }
190
237
 
191
238
  /**
192
- * Refresh all references and return head reference.
193
- * @return {DoubleLinker}
239
+ * Refresh all references (the head, the end and the length) by walking the list once, and return the head. The list's
240
+ * own methods keep these up to date, so this is only needed after linkers were changed directly.
241
+ * @return {DoubleLinker|null}
194
242
  */
195
243
  reset () {
196
244
  // Start at the pointer for the list
197
245
  let pointer = this.innerList
198
246
  if (pointer === null) {
247
+ this.countCache = 0
248
+ this.tailCache = null
199
249
  return null
200
250
  }
201
- let next = pointer.next
202
- // Follow references till the end
203
- while (next !== null) {
204
- pointer = next
205
- next = pointer.next
206
- }
207
- let prev = pointer.prev
208
- // From final reference, follow references back to the beginning
209
- while (prev !== null) {
210
- pointer = prev
211
- prev = pointer.prev
251
+ // Follow references back to the beginning
252
+ while (pointer.prev !== null) {
253
+ pointer = pointer.prev
212
254
  }
213
- // All the live references should have been found, and we are pointing to the true head
255
+ // We are pointing to the true head, now count along to the end to find the tail and the length
214
256
  this.innerList = pointer
257
+ let count = 0
258
+ let tail = pointer
259
+ let current = pointer
260
+ while (current !== null) {
261
+ ++count
262
+ tail = current
263
+ current = current.next
264
+ }
265
+ this.countCache = count
266
+ this.tailCache = tail
215
267
  return pointer
216
268
  }
217
269
 
@@ -247,6 +299,7 @@ class DoublyLinkedList {
247
299
  * Be able to run forEach on this DoublyLinkedList to iterate over the DoubleLinker Items.
248
300
  * @param {forEachCallback} callback The function to call for-each double linker
249
301
  * @param {DoublyLinkedList} thisArg Optional, 'this' reference
302
+ * @return {DoublyLinkedList} The list which was iterated.
250
303
  */
251
304
  forEach (callback, thisArg = this) {
252
305
  return _LinkedList.LinkedList.prototype.forEach.call(this, callback, thisArg)
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.DoublyLinkedList=void 0,require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.for-each.js");var _DoubleLinker=require("./DoubleLinker"),_DoubleLinkerIterator=require("../../recipes/DoubleLinkerIterator"),_LinkedList=require("../linked-list/LinkedList");class DoublyLinkedList{constructor(e=_DoubleLinker.DoubleLinker){this.classType=DoublyLinkedList,this.innerList=null,this.initialized=!1,this.linkerClass=e}initialize(e){return _LinkedList.LinkedList.prototype.initialize.call(this,e)}get list(){return this.innerList}get first(){return this.reset()}get last(){let e=this.innerList;if(null===e)return null;let t=e.next;for(;null!==t;)e=t,t=e.next;return e}get length(){let e=this.first,t=0;for(;null!==e;)++t,e=e.next;return t}insertAfter(e,t){return t=this.linkerClass.make(t),null!==e&&(t.next=e.next,t.prev=e,e.next=t),t.next&&(t.next.prev=t),this.length||(this.innerList=t),this.reset(),this}insertBefore(e,t){return t=this.linkerClass.make(t),null!==e&&(t.prev=e.prev,t.next=e,e.prev=t),t.prev&&(t.prev.next=t),this.length||(this.innerList=t),this.reset(),this}append(e,t=this.last){return this.insertAfter(t,e)}prepend(e,t=this.first){return this.insertBefore(t,e)}remove(e){return null===e?null:(e.prev&&(e.prev.next=e.next),e.next&&(e.next.prev=e.prev),this.innerList===e&&(this.innerList=e.next||e.prev||null),this.reset(),e)}reset(){let e=this.innerList;if(null===e)return null;let t=e.next;for(;null!==t;)e=t,t=e.next;let r=e.prev;for(;null!==r;)e=r,r=e.prev;return this.innerList=e,e}item(e){if(e>=0){let t=this.first,r=-1;for(;++r<e&&null!==t;)t=t.next;return r===e?t:null}let t=this.last,r=this.length;const i=this.length+e;if(i<0)return null;for(;--r>i&&null!==t;)t=t.prev;return r===i?t:null}forEach(e,t=this){return _LinkedList.LinkedList.prototype.forEach.call(this,e,t)}[Symbol.iterator](){let e=this.first;return new _DoubleLinkerIterator.DoubleLinkerIterator(e)}}exports.DoublyLinkedList=DoublyLinkedList,DoublyLinkedList.fromArray=(e=[],t=_DoubleLinker.DoubleLinker,r=DoublyLinkedList)=>_LinkedList.LinkedList.fromArray(e,t,r);
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.DoublyLinkedList=void 0,require("core-js/modules/esnext.iterator.constructor.js"),require("core-js/modules/esnext.iterator.for-each.js");var _DoubleLinker=require("./DoubleLinker"),_DoubleLinkerIterator=require("../../recipes/DoubleLinkerIterator"),_LinkedList=require("../linked-list/LinkedList");class DoublyLinkedList{constructor(e=_DoubleLinker.DoubleLinker){this.classType=DoublyLinkedList,this.innerList=null,this.initialized=!1,this.tailCache=null,this.countCache=null,this.linkerClass=e}initialize(e){return _LinkedList.LinkedList.prototype.initialize.call(this,e)}get list(){return this.innerList}get first(){let e=this.innerList;if(null===e)return null;for(;null!==e.prev;)e=e.prev;return this.innerList=e,e}get last(){if(null===this.innerList)return null;let e=null!==this.tailCache?this.tailCache:this.innerList;for(;null!==e.next;)e=e.next;return this.tailCache=e,e}get length(){return null===this.countCache&&this.reset(),this.countCache}insertAfter(e,t){if(t=this.linkerClass.make(t,this.linkerClass),null==e){const e=this.first;t.prev=null,t.next=e,e?e.prev=t:this.tailCache=t,this.innerList=t}else t.next=e.next,t.prev=e,e.next=t,t.next?t.next.prev=t:this.tailCache=t;return null!==this.countCache&&++this.countCache,this}insertBefore(e,t){if(t=this.linkerClass.make(t,this.linkerClass),null==e){const e=this.last;t.next=null,t.prev=e,null===e?this.innerList=t:e.next=t,this.tailCache=t}else t.prev=e.prev,t.next=e,e.prev=t,t.prev?t.prev.next=t:this.innerList=t;return null!==this.countCache&&++this.countCache,this}append(e,t=this.last){return this.insertAfter(t,e)}prepend(e,t=this.first){return this.insertBefore(t,e)}remove(e){return null==e?null:(e.prev&&(e.prev.next=e.next),e.next&&(e.next.prev=e.prev),this.innerList===e&&(this.innerList=e.next||e.prev||null),this.tailCache===e&&(this.tailCache=e.prev),null===this.innerList&&(this.tailCache=null),null!==this.countCache&&--this.countCache,e)}reset(){let e=this.innerList;if(null===e)return this.countCache=0,this.tailCache=null,null;for(;null!==e.prev;)e=e.prev;this.innerList=e;let t=0,i=e,n=e;for(;null!==n;)++t,i=n,n=n.next;return this.countCache=t,this.tailCache=i,e}item(e){if(e>=0){let t=this.first,i=-1;for(;++i<e&&null!==t;)t=t.next;return i===e?t:null}let t=this.last,i=this.length;const n=this.length+e;if(n<0)return null;for(;--i>n&&null!==t;)t=t.prev;return i===n?t:null}forEach(e,t=this){return _LinkedList.LinkedList.prototype.forEach.call(this,e,t)}[Symbol.iterator](){let e=this.first;return new _DoubleLinkerIterator.DoubleLinkerIterator(e)}}exports.DoublyLinkedList=DoublyLinkedList,DoublyLinkedList.fromArray=(e=[],t=_DoubleLinker.DoubleLinker,i=DoublyLinkedList)=>_LinkedList.LinkedList.fromArray(e,t,i);
@@ -12,12 +12,21 @@ import { Linker } from './Linker';
12
12
  * @extends Arrayable
13
13
  */
14
14
  export declare class LinkedList implements IsArrayable<Linker>, Iterable<Linker> {
15
+ /** The class used to create this instance, so that it can be recognized as valid without an instanceof check. */
15
16
  readonly classType: typeof LinkedList;
17
+ /** The first linker of the list (null when the list is empty), from which the whole list is reached. */
16
18
  innerList: Linker;
19
+ /** Whether the inner list has been initialized (it can only be initialized once). */
17
20
  initialized: boolean;
21
+ /** The class used to wrap the data given to this list as linkers. */
18
22
  linkerClass: typeof Linker;
23
+ /** The last linker, remembered so that adding to the end does not need to walk the whole list (null when not known yet). */
24
+ private tailCache;
25
+ /** The number of linkers, kept up to date by the list's own methods so that the length does not need to walk the whole list (null when not known yet). */
26
+ private countCache;
19
27
  /**
20
28
  * Create the new LinkedList instance.
29
+ * @param {Linker} [linkerClass=Linker] The class used to wrap given data as linkers.
21
30
  */
22
31
  constructor(linkerClass?: typeof Linker);
23
32
  /**
@@ -27,7 +36,7 @@ export declare class LinkedList implements IsArrayable<Linker>, Iterable<Linker>
27
36
  */
28
37
  initialize(initialList: Linker): LinkedList;
29
38
  /**
30
- * Retrieve a copy of the innerList used.
39
+ * Retrieve the innerList used (the list itself, not a copy).
31
40
  * @returns {Linker}
32
41
  */
33
42
  get list(): IsLinker;
@@ -37,29 +46,31 @@ export declare class LinkedList implements IsArrayable<Linker>, Iterable<Linker>
37
46
  */
38
47
  get first(): Linker;
39
48
  /**
40
- * Retrieve the last Linker in the list.
49
+ * Retrieve the last Linker in the list. The end is remembered, so this does not walk the list.
41
50
  * @returns {Linker}
42
51
  */
43
- get last(): Linker;
52
+ get last(): Linker | null;
44
53
  /**
45
- * Return the length of the list.
54
+ * Return the length of the list. It is kept up to date by the list's own methods, so this does not walk the list
55
+ * (call reset() after linkers were changed directly).
46
56
  * @returns {number}
47
57
  */
48
58
  get length(): number;
49
59
  /**
50
60
  * Insert a new node (or data) after a node.
51
- * @param {Linker|*} node The existing node as reference
61
+ * @param {Linker|*} node The existing node as reference, or null to insert at the start of the list
52
62
  * @param {Linker|*} newNode The new node to go after the existing node
53
63
  * @returns {LinkedList}
54
64
  */
55
- insertAfter(node: IsLinker, newNode: Linker | any): LinkedList;
65
+ insertAfter(node: IsLinker | null, newNode: Linker | any): LinkedList;
56
66
  /**
57
67
  * Insert a new node (or data) before a node.
58
- * @param {Linker|*} node The existing node as reference
68
+ * @param {Linker|*} node The existing node as reference, or null to insert at the end of the list
59
69
  * @param {Linker|*} newNode The new node to go before the existing node
60
70
  * @returns {LinkedList}
71
+ * @throws {Error} When the reference node is not in this list
61
72
  */
62
- insertBefore(node: IsLinker, newNode: Linker | any): LinkedList;
73
+ insertBefore(node: IsLinker | null, newNode: Linker | any): LinkedList;
63
74
  /**
64
75
  * Add a node (or data) after the given (or last) node in the list.
65
76
  * @param {Linker|*} node The new node to add to the end of the list
@@ -77,9 +88,15 @@ export declare class LinkedList implements IsArrayable<Linker>, Iterable<Linker>
77
88
  /**
78
89
  * Remove a linker from this linked list.
79
90
  * @param {Linker} node The node we wish to remove (and it will be returned after removal)
80
- * @return {Linker}
91
+ * @return {Linker|null} The removed node, or null when it was not in this list (nothing is removed)
81
92
  */
82
- remove(node: Linker): Linker;
93
+ remove(node: Linker | null): Linker | null;
94
+ /**
95
+ * Refresh the remembered end and length of the list by walking it once. The list's own methods keep these up to date,
96
+ * so this is only needed after linkers were changed directly (for example by setting next on a linker).
97
+ * @return {Linker|null} The first linker of the list
98
+ */
99
+ reset(): Linker | null;
83
100
  /**
84
101
  * Retrieve a Linker item from this list by numeric index, otherwise return null.
85
102
  * @param {number} index The integer number for retrieving a node by position.
@@ -14,11 +14,19 @@ var _Arrayable = require('../arrayable/Arrayable')
14
14
  class LinkedList {
15
15
  /**
16
16
  * Create the new LinkedList instance.
17
+ * @param {Linker} [linkerClass=Linker] The class used to wrap given data as linkers.
17
18
  */
18
19
  constructor (linkerClass = _Linker.Linker) {
20
+ /** The class used to create this instance, so that it can be recognized as valid without an instanceof check. */
19
21
  this.classType = LinkedList
22
+ /** The first linker of the list (null when the list is empty), from which the whole list is reached. */
20
23
  this.innerList = null
24
+ /** Whether the inner list has been initialized (it can only be initialized once). */
21
25
  this.initialized = false
26
+ /** The last linker, remembered so that adding to the end does not need to walk the whole list (null when not known yet). */
27
+ this.tailCache = null
28
+ /** The number of linkers, kept up to date by the list's own methods so that the length does not need to walk the whole list (null when not known yet). */
29
+ this.countCache = null
22
30
  this.linkerClass = linkerClass
23
31
  }
24
32
 
@@ -28,11 +36,12 @@ class LinkedList {
28
36
  * @return {LinkedList}
29
37
  */
30
38
  initialize (initialList) {
39
+ // Borrowed from Arrayable, which types its return as an Arrayable although it returns whatever list called it
31
40
  return _Arrayable.Arrayable.prototype.initialize.call(this, initialList)
32
41
  }
33
42
 
34
43
  /**
35
- * Retrieve a copy of the innerList used.
44
+ * Retrieve the innerList used (the list itself, not a copy).
36
45
  * @returns {Linker}
37
46
  */
38
47
  get list () {
@@ -48,78 +57,100 @@ class LinkedList {
48
57
  }
49
58
 
50
59
  /**
51
- * Retrieve the last Linker in the list.
60
+ * Retrieve the last Linker in the list. The end is remembered, so this does not walk the list.
52
61
  * @returns {Linker}
53
62
  */
54
63
  get last () {
55
- let tail = this.innerList
56
- if (tail === null) {
64
+ if (this.innerList === null) {
57
65
  return null
58
66
  }
59
- let next = tail.next
60
- while (next !== null) {
61
- tail = next
62
- next = tail.next
67
+ let tail = this.tailCache !== null ? this.tailCache : this.innerList
68
+ // The remembered tail is normally the end already, walking on from it also finds anything linked on outside of this list
69
+ while (tail.next !== null) {
70
+ tail = tail.next
63
71
  }
72
+ this.tailCache = tail
64
73
  return tail
65
74
  }
66
75
 
67
76
  /**
68
- * Return the length of the list.
77
+ * Return the length of the list. It is kept up to date by the list's own methods, so this does not walk the list
78
+ * (call reset() after linkers were changed directly).
69
79
  * @returns {number}
70
80
  */
71
81
  get length () {
72
- let current = this.first
73
- let length = 0
74
- while (current !== null) {
75
- ++length
76
- current = current.next
82
+ if (this.countCache === null) {
83
+ this.reset()
77
84
  }
78
- return length
85
+ return this.countCache
79
86
  }
80
87
 
81
88
  /**
82
89
  * Insert a new node (or data) after a node.
83
- * @param {Linker|*} node The existing node as reference
90
+ * @param {Linker|*} node The existing node as reference, or null to insert at the start of the list
84
91
  * @param {Linker|*} newNode The new node to go after the existing node
85
92
  * @returns {LinkedList}
86
93
  */
87
94
  insertAfter (node, newNode) {
88
- newNode = this.linkerClass.make(newNode)
89
- if (node !== null) {
90
- // Ensure the next reference of this node is assigned to the new node
95
+ newNode = this.linkerClass.make(newNode, this.linkerClass)
96
+ if (node === null || typeof node === 'undefined') {
97
+ // After nothing means at the start of the list
98
+ newNode.next = this.innerList
99
+ if (this.innerList === null) {
100
+ this.tailCache = newNode
101
+ }
102
+ this.innerList = newNode
103
+ } else {
91
104
  newNode.next = node.next
92
- // Then set this node's next reference to the new node
93
105
  node.next = newNode
106
+ if (newNode.next === null) {
107
+ this.tailCache = newNode
108
+ }
94
109
  }
95
- if (!this.length) {
96
- this.innerList = newNode
110
+ if (this.countCache !== null) {
111
+ ++this.countCache
97
112
  }
98
113
  return this
99
114
  }
100
115
 
101
116
  /**
102
117
  * Insert a new node (or data) before a node.
103
- * @param {Linker|*} node The existing node as reference
118
+ * @param {Linker|*} node The existing node as reference, or null to insert at the end of the list
104
119
  * @param {Linker|*} newNode The new node to go before the existing node
105
120
  * @returns {LinkedList}
121
+ * @throws {Error} When the reference node is not in this list
106
122
  */
107
123
  insertBefore (node, newNode) {
108
- newNode = this.linkerClass.make(newNode)
109
- let prevNode = null
110
- let currentNode = this.first
111
- while (currentNode !== node) {
112
- prevNode = currentNode
113
- currentNode = currentNode.next
114
- }
115
- // The new node will reference this node as next
116
- newNode.next = node
117
- if (prevNode) {
118
- // Ensure the next reference of the previous node is assigned to the new node
119
- prevNode.next = newNode
124
+ newNode = this.linkerClass.make(newNode, this.linkerClass)
125
+ if (node === null || typeof node === 'undefined') {
126
+ // Before nothing means at the end of the list
127
+ const tail = this.last
128
+ newNode.next = null
129
+ if (tail === null) {
130
+ this.innerList = newNode
131
+ } else {
132
+ tail.next = newNode
133
+ }
134
+ this.tailCache = newNode
135
+ } else {
136
+ let prevNode = null
137
+ let currentNode = this.first
138
+ while (currentNode !== null && currentNode !== node) {
139
+ prevNode = currentNode
140
+ currentNode = currentNode.next
141
+ }
142
+ if (currentNode === null) {
143
+ throw new Error('The reference node is not in this list.')
144
+ }
145
+ newNode.next = node
146
+ if (prevNode) {
147
+ prevNode.next = newNode
148
+ } else {
149
+ this.innerList = newNode
150
+ }
120
151
  }
121
- if (node === this.first || node === null) {
122
- this.innerList = newNode
152
+ if (this.countCache !== null) {
153
+ ++this.countCache
123
154
  }
124
155
  return this
125
156
  }
@@ -147,26 +178,58 @@ class LinkedList {
147
178
  /**
148
179
  * Remove a linker from this linked list.
149
180
  * @param {Linker} node The node we wish to remove (and it will be returned after removal)
150
- * @return {Linker}
181
+ * @return {Linker|null} The removed node, or null when it was not in this list (nothing is removed)
151
182
  */
152
183
  remove (node) {
184
+ if (node === null || typeof node === 'undefined') {
185
+ return null
186
+ }
153
187
  let prevNode = null
154
188
  let currentNode = this.first
155
- while (currentNode !== node) {
189
+ while (currentNode !== null && currentNode !== node) {
156
190
  prevNode = currentNode
157
191
  currentNode = currentNode.next
158
192
  }
193
+ if (currentNode === null) {
194
+ // The node is not in this list, so there is nothing to remove
195
+ return null
196
+ }
159
197
  if (prevNode) {
160
- // Ensure the next reference of the previous node skips over the removed node
161
198
  prevNode.next = node.next
162
- }
163
- if (node === this.first && node !== null) {
164
- // Update list head to point to next if it was this node
199
+ } else {
165
200
  this.innerList = node.next
166
201
  }
202
+ if (this.tailCache === node) {
203
+ this.tailCache = prevNode
204
+ }
205
+ if (this.innerList === null) {
206
+ this.tailCache = null
207
+ }
208
+ if (this.countCache !== null) {
209
+ --this.countCache
210
+ }
167
211
  return node
168
212
  }
169
213
 
214
+ /**
215
+ * Refresh the remembered end and length of the list by walking it once. The list's own methods keep these up to date,
216
+ * so this is only needed after linkers were changed directly (for example by setting next on a linker).
217
+ * @return {Linker|null} The first linker of the list
218
+ */
219
+ reset () {
220
+ let count = 0
221
+ let tail = null
222
+ let current = this.innerList
223
+ while (current !== null) {
224
+ ++count
225
+ tail = current
226
+ current = current.next
227
+ }
228
+ this.countCache = count
229
+ this.tailCache = tail
230
+ return this.innerList
231
+ }
232
+
170
233
  /**
171
234
  * Retrieve a Linker item from this list by numeric index, otherwise return null.
172
235
  * @param {number} index The integer number for retrieving a node by position.
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.LinkedList=void 0;var _Linker=require("./Linker"),_LinkerIterator=require("../../recipes/LinkerIterator"),_Arrayable=require("../arrayable/Arrayable");class LinkedList{constructor(t=_Linker.Linker){this.classType=LinkedList,this.innerList=null,this.initialized=!1,this.linkerClass=t}initialize(t){return _Arrayable.Arrayable.prototype.initialize.call(this,t)}get list(){return this.innerList}get first(){return this.innerList}get last(){let t=this.innerList;if(null===t)return null;let e=t.next;for(;null!==e;)t=e,e=t.next;return t}get length(){let t=this.first,e=0;for(;null!==t;)++e,t=t.next;return e}insertAfter(t,e){return e=this.linkerClass.make(e),null!==t&&(e.next=t.next,t.next=e),this.length||(this.innerList=e),this}insertBefore(t,e){e=this.linkerClass.make(e);let r=null,i=this.first;for(;i!==t;)r=i,i=i.next;return e.next=t,r&&(r.next=e),t!==this.first&&null!==t||(this.innerList=e),this}append(t,e=this.last){return this.insertAfter(e,t)}prepend(t,e=this.first){return this.insertBefore(e,t)}remove(t){let e=null,r=this.first;for(;r!==t;)e=r,r=r.next;return e&&(e.next=t.next),t===this.first&&null!==t&&(this.innerList=t.next),t}item(t){if(t>=0){let e=this.first,r=-1;for(;++r<t&&null!==e;)e=e.next;return r===t?e:null}let e=this.first,r=0;const i=this.length+t;if(i<0)return null;for(;r<i&&null!==e;)e=e.next,++r;return r===i?e:null}forEach(t,e=this){let r=0,i=e.first;for(;null!==i;)t(i,r,e),i=i.next,++r;return e}[Symbol.iterator](){return new _LinkerIterator.LinkerIterator(this.first)}}exports.LinkedList=LinkedList,LinkedList.fromArray=(t=[],e=_Linker.Linker,r=LinkedList)=>new r(e).initialize(e.fromArray(t).head);
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.LinkedList=void 0;var _Linker=require("./Linker"),_LinkerIterator=require("../../recipes/LinkerIterator"),_Arrayable=require("../arrayable/Arrayable");class LinkedList{constructor(t=_Linker.Linker){this.classType=LinkedList,this.innerList=null,this.initialized=!1,this.tailCache=null,this.countCache=null,this.linkerClass=t}initialize(t){return _Arrayable.Arrayable.prototype.initialize.call(this,t)}get list(){return this.innerList}get first(){return this.innerList}get last(){if(null===this.innerList)return null;let t=null!==this.tailCache?this.tailCache:this.innerList;for(;null!==t.next;)t=t.next;return this.tailCache=t,t}get length(){return null===this.countCache&&this.reset(),this.countCache}insertAfter(t,e){return e=this.linkerClass.make(e,this.linkerClass),null==t?(e.next=this.innerList,null===this.innerList&&(this.tailCache=e),this.innerList=e):(e.next=t.next,t.next=e,null===e.next&&(this.tailCache=e)),null!==this.countCache&&++this.countCache,this}insertBefore(t,e){if(e=this.linkerClass.make(e,this.linkerClass),null==t){const t=this.last;e.next=null,null===t?this.innerList=e:t.next=e,this.tailCache=e}else{let i=null,n=this.first;for(;null!==n&&n!==t;)i=n,n=n.next;if(null===n)throw new Error("The reference node is not in this list.");e.next=t,i?i.next=e:this.innerList=e}return null!==this.countCache&&++this.countCache,this}append(t,e=this.last){return this.insertAfter(e,t)}prepend(t,e=this.first){return this.insertBefore(e,t)}remove(t){if(null==t)return null;let e=null,i=this.first;for(;null!==i&&i!==t;)e=i,i=i.next;return null===i?null:(e?e.next=t.next:this.innerList=t.next,this.tailCache===t&&(this.tailCache=e),null===this.innerList&&(this.tailCache=null),null!==this.countCache&&--this.countCache,t)}reset(){let t=0,e=null,i=this.innerList;for(;null!==i;)++t,e=i,i=i.next;return this.countCache=t,this.tailCache=e,this.innerList}item(t){if(t>=0){let e=this.first,i=-1;for(;++i<t&&null!==e;)e=e.next;return i===t?e:null}let e=this.first,i=0;const n=this.length+t;if(n<0)return null;for(;i<n&&null!==e;)e=e.next,++i;return i===n?e:null}forEach(t,e=this){let i=0,n=e.first;for(;null!==n;)t(n,i,e),n=n.next,++i;return e}[Symbol.iterator](){return new _LinkerIterator.LinkerIterator(this.first)}}exports.LinkedList=LinkedList,LinkedList.fromArray=(t=[],e=_Linker.Linker,i=LinkedList)=>new i(e).initialize(e.fromArray(t).head);
@@ -10,12 +10,15 @@ import { IsLinker } from '../../recipes/IsLinker';
10
10
  * @extends ArrayElement
11
11
  */
12
12
  export declare class Linker implements IsLinker {
13
+ /** The class used to create this instance, so that it can be recognized as valid without an instanceof check. */
13
14
  readonly classType: typeof Linker;
15
+ /** The data stored in this linker. */
14
16
  data: any;
17
+ /** The linker after this one, or null when this is the last. */
15
18
  next: Linker | null;
16
19
  /**
17
20
  * Create the new Linker instance, provide the data and optionally give the next Linker.
18
- * @param {Object} [nodeData={}]
21
+ * @param {Object} [nodeData={}] The settings for the new linker.
19
22
  * @param {*} [nodeData.data=null] The data to be stored in this linker
20
23
  * @param {Linker|null} [nodeData.next=null] The reference to the next linker if any
21
24
  */
@@ -36,7 +39,7 @@ export declare class Linker implements IsLinker {
36
39
  * @param {IsLinker} [classType=Linker] Provide the type of IsLinker to use.
37
40
  * @returns {{head: Linker, tail: Linker}}
38
41
  */
39
- static fromArray: (values: Array<any>, classType?: any) => {
42
+ static fromArray: (values?: Array<any>, classType?: any) => {
40
43
  head: IsLinker;
41
44
  tail: IsLinker;
42
45
  };