list-toolkit 1.0.1 → 2.0.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 (86) hide show
  1. package/README.md +93 -407
  2. package/cjs/cache/cache-fifo.js +37 -0
  3. package/cjs/cache/cache-lfu.js +76 -0
  4. package/cjs/cache/cache-lru.js +100 -0
  5. package/cjs/cache/cache-random.js +77 -0
  6. package/cjs/cache/decorator.js +47 -0
  7. package/cjs/cache.js +28 -0
  8. package/cjs/ext-list.js +22 -0
  9. package/cjs/ext-slist.js +22 -0
  10. package/cjs/ext-value-list.js +22 -0
  11. package/cjs/ext-value-slist.js +22 -0
  12. package/cjs/{MinHeap.js → heap/min-heap.js} +77 -10
  13. package/cjs/heap.js +22 -0
  14. package/cjs/list/basics.js +88 -0
  15. package/cjs/list/core.js +305 -0
  16. package/cjs/list/ext-value.js +89 -0
  17. package/cjs/list/ext.js +356 -0
  18. package/cjs/list/nodes.js +240 -0
  19. package/cjs/list/ptr.js +61 -0
  20. package/cjs/list/value.js +100 -0
  21. package/cjs/list-helpers.js +91 -0
  22. package/cjs/list-utils.js +141 -0
  23. package/cjs/list.js +22 -0
  24. package/cjs/meta-utils.js +167 -0
  25. package/cjs/nt-utils.js +132 -0
  26. package/cjs/queue.js +58 -0
  27. package/cjs/slist/basics.js +71 -0
  28. package/cjs/slist/core.js +362 -0
  29. package/cjs/slist/ext-value.js +83 -0
  30. package/cjs/slist/ext.js +336 -0
  31. package/cjs/slist/nodes.js +276 -0
  32. package/cjs/slist/ptr.js +87 -0
  33. package/cjs/slist/value.js +91 -0
  34. package/cjs/slist.js +22 -0
  35. package/cjs/stack.js +55 -0
  36. package/cjs/value-list.js +22 -0
  37. package/cjs/value-slist.js +22 -0
  38. package/package.json +10 -8
  39. package/src/cache/cache-fifo.js +27 -0
  40. package/src/cache/cache-lfu.js +63 -0
  41. package/src/cache/cache-lru.js +87 -0
  42. package/src/cache/cache-random.js +73 -0
  43. package/src/cache/decorator.js +45 -0
  44. package/src/cache.js +9 -0
  45. package/src/ext-list.js +6 -0
  46. package/src/ext-slist.js +6 -0
  47. package/src/ext-value-list.js +6 -0
  48. package/src/ext-value-slist.js +6 -0
  49. package/src/{MinHeap.js → heap/min-heap.js} +91 -9
  50. package/src/heap.js +6 -0
  51. package/src/list/basics.js +64 -0
  52. package/src/list/core.js +314 -0
  53. package/src/list/ext-value.js +81 -0
  54. package/src/list/ext.js +370 -0
  55. package/src/list/nodes.js +262 -0
  56. package/src/list/ptr.js +58 -0
  57. package/src/list/value.js +88 -0
  58. package/src/list-helpers.js +80 -0
  59. package/src/list-utils.js +140 -0
  60. package/src/list.js +6 -0
  61. package/src/meta-utils.js +147 -0
  62. package/src/nt-utils.js +85 -0
  63. package/src/queue.js +52 -0
  64. package/src/slist/basics.js +47 -0
  65. package/src/slist/core.js +364 -0
  66. package/src/slist/ext-value.js +74 -0
  67. package/src/slist/ext.js +331 -0
  68. package/src/slist/nodes.js +290 -0
  69. package/src/slist/ptr.js +77 -0
  70. package/src/slist/value.js +75 -0
  71. package/src/slist.js +6 -0
  72. package/src/stack.js +52 -0
  73. package/src/value-list.js +6 -0
  74. package/src/value-slist.js +6 -0
  75. package/cjs/Cache.js +0 -70
  76. package/cjs/List.js +0 -291
  77. package/cjs/ListHead.js +0 -308
  78. package/cjs/SList.js +0 -336
  79. package/cjs/SListHead.js +0 -363
  80. package/cjs/utils.js +0 -43
  81. package/src/Cache.js +0 -61
  82. package/src/List.js +0 -303
  83. package/src/ListHead.js +0 -304
  84. package/src/SList.js +0 -329
  85. package/src/SListHead.js +0 -353
  86. package/src/utils.js +0 -35
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- import {copyOptions} from './utils.js';
3
+ import {copyOptions} from '../meta-utils.js';
4
4
 
5
5
  // the following functions are inlined:
6
6
 
@@ -45,7 +45,7 @@ const down = (array, i, less = defaultLess, n = array.length) => {
45
45
  return array;
46
46
  };
47
47
 
48
- class MinHeap {
48
+ export class MinHeap {
49
49
  constructor(options, ...args) {
50
50
  copyOptions(this, MinHeap.defaults, options);
51
51
  this.array = [];
@@ -64,9 +64,8 @@ class MinHeap {
64
64
  return this.array[0];
65
65
  }
66
66
 
67
- clear() {
68
- this.array = [];
69
- return this;
67
+ peek() {
68
+ return this.array[0];
70
69
  }
71
70
 
72
71
  pop() {
@@ -175,6 +174,50 @@ class MinHeap {
175
174
  return top;
176
175
  }
177
176
 
177
+ has(value) {
178
+ // return MinHeap.has(this.array, value, this.equal); // inlined
179
+ return this.array.findIndex(element => this.equal(element, value)) >= 0;
180
+ }
181
+
182
+ findIndex(value) {
183
+ return this.array.findIndex(element => this.equal(element, value));
184
+ }
185
+
186
+ remove(value) {
187
+ MinHeap.remove(this.array, value, this.less, this.equal);
188
+ return this;
189
+ }
190
+
191
+ removeByIndex(index) {
192
+ MinHeap.removeByIndex(this.array, index, this.less);
193
+ return this;
194
+ }
195
+
196
+ replace(value, newValue) {
197
+ MinHeap.replace(this.array, value, newValue, this.less, this.equal);
198
+ return this;
199
+ }
200
+
201
+ replaceByIndex(index, newValue) {
202
+ MinHeap.replaceByIndex(this.array, index, newValue, this.less);
203
+ return this;
204
+ }
205
+
206
+ updateTop() {
207
+ down(this.array, 0, this.less);
208
+ return this;
209
+ }
210
+
211
+ updateByIndex(index, isDecreased) {
212
+ MinHeap.updateByIndex(this.array, index, isDecreased, this.less);
213
+ return this;
214
+ }
215
+
216
+ clear() {
217
+ this.array = [];
218
+ return this;
219
+ }
220
+
178
221
  releaseSorted() {
179
222
  MinHeap.sort(this.array, this.less);
180
223
  const array = this.array;
@@ -255,10 +298,7 @@ class MinHeap {
255
298
 
256
299
  static pushPop(heapArray, item, less = MinHeap.defaults.less) {
257
300
  if (!heapArray.length || less(item, heapArray[0])) return item;
258
- const top = heapArray[0];
259
- heapArray[0] = item;
260
- down(heapArray, 0, less);
261
- return top;
301
+ return MinHeap.replaceTop(heapArray, item, less);
262
302
  }
263
303
 
264
304
  static replaceTop(heapArray, item, less = MinHeap.defaults.less) {
@@ -268,6 +308,48 @@ class MinHeap {
268
308
  return top;
269
309
  }
270
310
 
311
+ static has(heapArray, item, equal = MinHeap.defaults.equal) {
312
+ return heapArray.findIndex(element => equal(element, item)) >= 0;
313
+ }
314
+
315
+ static findIndex(heapArray, item, equal = MinHeap.defaults.equal) {
316
+ return heapArray.findIndex(element => equal(element, item));
317
+ }
318
+
319
+ static removeByIndex(heapArray, index, less = MinHeap.defaults.less) {
320
+ if (index < 0 || index >= heapArray.length) return this;
321
+ const last = heapArray.length - 1;
322
+ if (index !== last) {
323
+ const item = heapArray[index],
324
+ newItem = heapArray[index] = heapArray.pop();
325
+ return MinHeap.updateByIndex(heapArray, index, less(newItem, item), less);
326
+ }
327
+ heapArray.pop();
328
+ return heapArray;
329
+ }
330
+
331
+ static remove(heapArray, item, less = MinHeap.defaults.less, equal = MinHeap.defaults.equal) {
332
+ const index = heapArray.findIndex(element => equal(element, item));
333
+ return MinHeap.removeByIndex(heapArray, index, less);
334
+ }
335
+
336
+ static replaceByIndex(heapArray, index, newItem, less = MinHeap.defaults.less) {
337
+ if (index < 0 || index >= heapArray.length) return this;
338
+ const item = heapArray[index];
339
+ heapArray[index] = newItem;
340
+ return MinHeap.updateByIndex(heapArray, index, less(newItem, item), less);
341
+ }
342
+
343
+ static replace(heapArray, item, newItem, less = MinHeap.defaults.less, equal = MinHeap.defaults.equal) {
344
+ const index = heapArray.findIndex(element => equal(element, item));
345
+ return MinHeap.replaceByIndex(heapArray, index, newItem, less);
346
+ }
347
+
348
+ static updateByIndex(heapArray, index, isDecreased, less = MinHeap.defaults.less) {
349
+ if (index < 0 || index >= heapArray.length) return this;
350
+ return (isDecreased ? up : down)(heapArray, index, less);
351
+ }
352
+
271
353
  static sort(heapArray, less = MinHeap.defaults.less) {
272
354
  if (heapArray.length <= 1) return heapArray;
273
355
  for (let n = heapArray.length - 1; n; --n) {
package/src/heap.js ADDED
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+
3
+ export * from './heap/min-heap.js';
4
+ import Heap from './heap/min-heap.js';
5
+
6
+ export default Heap;
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+
3
+ // useful low-level operations on doubly linked lists
4
+
5
+ export const extract = ({nextName, prevName}, {from, to = from}) => {
6
+ const next = to[nextName],
7
+ prev = from[prevName];
8
+
9
+ // extract
10
+ prev[nextName] = next;
11
+ next[prevName] = prev;
12
+
13
+ // clear
14
+ from[prevName] = to;
15
+ to[nextName] = from;
16
+
17
+ return {extracted: from, rest: next === from ? null : next};
18
+ };
19
+
20
+ // pop(options, head).node === extract(options, {from: node})
21
+
22
+ export const pop = ({nextName, prevName}, node) => {
23
+ const next = node[nextName],
24
+ prev = node[prevName];
25
+
26
+ // extract
27
+ prev[nextName] = next;
28
+ next[prevName] = prev;
29
+
30
+ // clear
31
+ node[prevName] = node[nextName] = node;
32
+
33
+ return {extracted: node, rest: next === node ? null : next};
34
+ };
35
+
36
+ export const splice = ({nextName, prevName}, target, circularList) => {
37
+ const next = target[nextName],
38
+ from = circularList,
39
+ to = from[prevName];
40
+
41
+ // splice
42
+ target[nextName] = from;
43
+ from[prevName] = target;
44
+ to[nextName] = next;
45
+ next[prevName] = to;
46
+
47
+ return target;
48
+ };
49
+
50
+ // append(options, target, range) === splice(options, target, extract(options, range))
51
+
52
+ export const append = ({nextName, prevName}, target, {from, to = from}) => {
53
+ // extract
54
+ from[prevName][nextName] = to[nextName];
55
+ to[nextName][prevName] = from[prevName];
56
+
57
+ // splice
58
+ to[nextName] = target[nextName];
59
+ to[nextName][prevName] = to;
60
+ target[nextName] = from;
61
+ from[prevName] = target;
62
+
63
+ return target;
64
+ };
@@ -0,0 +1,314 @@
1
+ 'use strict';
2
+
3
+ import {ExtListBase, HeadNode} from './nodes.js';
4
+ import {pop, splice, append} from './basics.js';
5
+ import Ptr from './ptr.js';
6
+ import {addAliases, mapIterator, normalizeIterator} from '../meta-utils.js';
7
+
8
+ export class List extends HeadNode {
9
+ get frontPtr() {
10
+ return new Ptr(this, this.front);
11
+ }
12
+
13
+ get backPtr() {
14
+ return new Ptr(this, this.back);
15
+ }
16
+
17
+ makePtr(node) {
18
+ if (node && !this.isNodeLike(node)) throw new Error('"node" is not a compatible node');
19
+ return new Ptr(this, node || this.front);
20
+ }
21
+
22
+ makePtrFromPrev(prev) {
23
+ if (prev && !this.isNodeLike(prev)) throw new Error('"prev" is not a compatible node');
24
+ return new Ptr(this, prev ? prev[this.nextName] : this.front);
25
+ }
26
+
27
+ pushFront(value) {
28
+ const node = this.adoptValue(value);
29
+ splice(this, this, node);
30
+ return this.makePtr(node);
31
+ }
32
+
33
+ pushBack(value) {
34
+ const node = this.adoptValue(value);
35
+ splice(this, this[this.prevName], node);
36
+ return this.makePtr(node);
37
+ }
38
+
39
+ popFrontNode() {
40
+ if (!this.isEmpty) return pop(this, this[this.nextName]).extracted;
41
+ }
42
+
43
+ popBackNode() {
44
+ if (!this.isEmpty) return pop(this, this[this.prevName]).extracted;
45
+ }
46
+
47
+ pushFrontNode(nodeOrPtr) {
48
+ const node = this.adoptNode(nodeOrPtr);
49
+ splice(this, this, node);
50
+ return this.makePtr(node);
51
+ }
52
+
53
+ pushBackNode(nodeOrPtr) {
54
+ const node = this.adoptNode(nodeOrPtr);
55
+ splice(this, this[this.prevName], node);
56
+ return this.makePtr(node);
57
+ }
58
+
59
+ appendFront(list) {
60
+ if (!this.isCompatible(list)) throw new Error('Incompatible lists');
61
+ if (list.isEmpty) return this;
62
+
63
+ const head = list[this.nextName];
64
+ append(this, this, {from: head, to: list[this.prevName]});
65
+
66
+ return this.makePtr();
67
+ }
68
+
69
+ appendBack(list) {
70
+ if (!this.isCompatible(list)) throw new Error('Incompatible lists');
71
+ if (list.isEmpty) return this;
72
+
73
+ const head = list[this.nextName];
74
+ append(this, this[this.prevName], {from: head, to: list[this.prevName]});
75
+
76
+ return this.makePtr(head);
77
+ }
78
+
79
+ moveToFront(nodeOrPtr) {
80
+ const node = this.normalizeNode(nodeOrPtr);
81
+ if (this[this.nextName] !== node) {
82
+ splice(this, this, pop(this, node).extracted);
83
+ }
84
+ return this.frontPtr;
85
+ }
86
+
87
+ moveToBack(nodeOrPtr) {
88
+ const node = this.normalizeNode(nodeOrPtr);
89
+ if (this[this.prevName] !== node) {
90
+ splice(this, this[this.prevName], pop(this, node).extracted);
91
+ }
92
+ return this.backPtr;
93
+ }
94
+
95
+ clear(drop) {
96
+ if (drop) {
97
+ while (!this.isEmpty) this.popFrontNode();
98
+ } else {
99
+ this[this.nextName] = this[this.prevName] = this;
100
+ }
101
+ return this;
102
+ }
103
+
104
+ removeNode(nodeOrPtr) {
105
+ return pop(this, this.normalizeNode(nodeOrPtr)).extracted;
106
+ }
107
+
108
+ removeRange(range, drop) {
109
+ return this.extractRange(range).clear(drop);
110
+ }
111
+
112
+ extractRange(range = {}) {
113
+ range = this.normalizeRange(range);
114
+ range.from ||= this.front;
115
+ range.to ||= this.back;
116
+ return append(this, this.make(), range);
117
+ }
118
+
119
+ extractBy(condition) {
120
+ const extracted = this.make();
121
+ if (this.isEmpty) return extracted;
122
+
123
+ while (this.isEmpty && condition(this.front)) extracted.pushBack(this.popFront());
124
+ if (this.isOneOrEmpty) return extracted;
125
+
126
+ for (const ptr of this.getPtrIterator({from: this.front[this.nextName]})) {
127
+ if (condition(ptr.node)) extracted.pushBack(ptr.removeCurrent());
128
+ }
129
+
130
+ return extracted;
131
+ }
132
+
133
+ reverse() {
134
+ let current = this;
135
+ do {
136
+ const next = current[this.nextName];
137
+ current[this.nextName] = current[this.prevName];
138
+ current[this.prevName] = next;
139
+ current = next;
140
+ } while (current !== this);
141
+ return this;
142
+ }
143
+
144
+ sort(lessFn) {
145
+ if (this.isOneOrEmpty) return this;
146
+
147
+ const left = this.make(),
148
+ right = this.make();
149
+
150
+ // split into two sublists
151
+ for (let isLeft = true; !this.isEmpty; isLeft = !isLeft) {
152
+ (isLeft ? left : right).pushBackNode(this.popFrontNode());
153
+ }
154
+ // the list is empty now
155
+
156
+ // sort sublists
157
+ left.sort(lessFn);
158
+ right.sort(lessFn);
159
+
160
+ // merge sublists
161
+ while (!left.isEmpty && !right.isEmpty) {
162
+ this.pushBackNode((lessFn(left.front, right.front) ? left : right).popFrontNode());
163
+ }
164
+ if (!left.isEmpty) this.appendBack(left);
165
+ if (!right.isEmpty) this.appendBack(right);
166
+
167
+ return this;
168
+ }
169
+
170
+ releaseRawList() {
171
+ return this.isEmpty ? null : pop(this, this).rest;
172
+ }
173
+
174
+ releaseNTList() {
175
+ if (this.isEmpty) return null;
176
+ const head = this[this.nextName],
177
+ tail = this[this.prevName];
178
+ this.clear();
179
+ head[this.prevName] = tail[this.nextName] = null;
180
+ return {head, tail};
181
+ }
182
+
183
+ validateRange(range = {}) {
184
+ range = this.normalizeRange(range);
185
+ let current = range.from;
186
+ do {
187
+ if (current === this) return false;
188
+ current = current[this.nextName];
189
+ } while (current !== range.to);
190
+ return true;
191
+ }
192
+
193
+ // iterators
194
+
195
+ [Symbol.iterator]() {
196
+ let current = this[this.nextName],
197
+ readyToStop = this.isEmpty;
198
+ return normalizeIterator({
199
+ next: () => {
200
+ if (readyToStop && current === this) return {done: true};
201
+ readyToStop = true;
202
+ const value = current;
203
+ current = current[this.nextName];
204
+ return {value};
205
+ }
206
+ });
207
+ }
208
+
209
+ getNodeIterator(range = {}) {
210
+ range = this.normalizeRange(range);
211
+ const {from, to} = range;
212
+ return {
213
+ [Symbol.iterator]: () => {
214
+ let current = from || this[this.nextName],
215
+ readyToStop = this.isEmpty;
216
+ const stop = to ? to[this.nextName] : this;
217
+ return normalizeIterator({
218
+ next: () => {
219
+ if (readyToStop && current === stop) return {done: true};
220
+ readyToStop = true;
221
+ const value = current;
222
+ current = current[this.nextName];
223
+ return {value};
224
+ }
225
+ });
226
+ }
227
+ };
228
+ }
229
+
230
+ getPtrIterator(range) {
231
+ return mapIterator(this.getNodeIterator(range), node => new Ptr(this, node));
232
+ }
233
+
234
+ getReverseNodeIterator(range = {}) {
235
+ range = this.normalizeRange(range);
236
+ const {from, to} = range;
237
+ return {
238
+ [Symbol.iterator]: () => {
239
+ let current = to || this[this.prevName],
240
+ readyToStop = this.isEmpty;
241
+ const stop = from ? from[this.prevName] : this;
242
+ return normalizeIterator({
243
+ next: () => {
244
+ if (readyToStop && current === stop) return {done: true};
245
+ readyToStop = true;
246
+ const value = current;
247
+ current = current[this.prevName];
248
+ return {value};
249
+ }
250
+ });
251
+ }
252
+ };
253
+ }
254
+
255
+ getReversePtrIterator(range) {
256
+ return mapIterator(this.getReverseNodeIterator(range), node => new Ptr(this, node));
257
+ }
258
+
259
+ // meta helpers
260
+
261
+ make() {
262
+ return new List(this);
263
+ }
264
+
265
+ makeFrom(values) {
266
+ return List.from(values, this);
267
+ }
268
+
269
+ makeFromRange(range) {
270
+ return List.fromRange(range, this);
271
+ }
272
+
273
+ static from(values, options) {
274
+ const list = new List(options);
275
+ for (const value of values) list.pushBack(value);
276
+ return list;
277
+ }
278
+
279
+ static fromRange(range, options) {
280
+ const list = new List(options);
281
+ if (!range) return list;
282
+
283
+ range = list.normalizeRange(range);
284
+ if (!range.from || !range.to) throw new Error('"range" should be fully specified');
285
+
286
+ return append(list, list, range);
287
+ }
288
+
289
+ static fromExtList(extList) {
290
+ if (!(extList instanceof ExtListBase)) throw new Error('Not a circular list');
291
+
292
+ const list = new List(extList);
293
+ if (extList.isEmpty) return list;
294
+
295
+ splice(list, list, extList.head);
296
+ extList.clear();
297
+
298
+ return list;
299
+ }
300
+ }
301
+
302
+ List.Ptr = Ptr;
303
+
304
+ addAliases(List.prototype, {
305
+ popFrontNode: 'popFront, pop',
306
+ popBackNode: 'popBack',
307
+ pushFront: 'push',
308
+ getNodeIterator: 'getIterator',
309
+ getReverseNodeIterator: 'getReverseIterator',
310
+ appendBack: 'append'
311
+ });
312
+
313
+ export {Ptr};
314
+ export default List;
@@ -0,0 +1,81 @@
1
+ 'use strict';
2
+
3
+ import ExtList, {Ptr} from './ext.js';
4
+ import {ValueNode} from './nodes.js';
5
+ import {addAliases, mapIterator, normalizeIterator} from '../meta-utils.js';
6
+
7
+ export class ExtValueList extends ExtList {
8
+ adoptValue(value) {
9
+ if (value instanceof Ptr) {
10
+ if (!this.isCompatiblePtr(value)) throw new Error('Incompatible pointer');
11
+ if (value.node instanceof ValueNode) {
12
+ value.list = this;
13
+ return super.adoptNode(value);
14
+ }
15
+ return new ValueNode(value.node, this);
16
+ }
17
+ if (value instanceof ValueNode) {
18
+ if (!this.isNodeLike(value)) throw new Error('Incompatible node');
19
+ return super.adoptNode(value);
20
+ }
21
+ return new ValueNode(value, this);
22
+ }
23
+
24
+ // iterators
25
+
26
+ [Symbol.iterator]() {
27
+ let current = this.head,
28
+ readyToStop = this.isEmpty;
29
+ return normalizeIterator({
30
+ next: () => {
31
+ if (readyToStop && current === this.head) return {done: true};
32
+ readyToStop = true;
33
+ const value = current.value;
34
+ current = current[this.nextName];
35
+ return {value};
36
+ }
37
+ });
38
+ }
39
+
40
+ getValueIterator(range) {
41
+ return mapIterator(this.getNodeIterator(range), node => node.value);
42
+ }
43
+
44
+ getReverseValueIterator(range) {
45
+ return mapIterator(this.getReverseNodeIterator(range), node => node.value);
46
+ }
47
+
48
+ // meta helpers
49
+
50
+ clone() {
51
+ return new ExtValueList(this);
52
+ }
53
+
54
+ make(head = null) {
55
+ return new ExtValueList(head, this);
56
+ }
57
+
58
+ makeFrom(values) {
59
+ return ExtValueList.from(values, this);
60
+ }
61
+
62
+ static from(values, options) {
63
+ const list = new ExtValueList(null, options);
64
+ for (const value of values) {
65
+ list.addAfter(value);
66
+ list.next();
67
+ }
68
+ return list.next();
69
+ }
70
+ }
71
+
72
+ ExtValueList.Ptr = Ptr;
73
+ ExtValueList.ValueNode = ValueNode;
74
+
75
+ addAliases(ExtValueList.prototype, {
76
+ getValueIterator: 'getIterator',
77
+ getReverseValueIterator: 'getReverseIterator'
78
+ });
79
+
80
+ export {ValueNode};
81
+ export default ExtValueList;