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.
- package/README.md +93 -407
- package/cjs/cache/cache-fifo.js +37 -0
- package/cjs/cache/cache-lfu.js +76 -0
- package/cjs/cache/cache-lru.js +100 -0
- package/cjs/cache/cache-random.js +77 -0
- package/cjs/cache/decorator.js +47 -0
- package/cjs/cache.js +28 -0
- package/cjs/ext-list.js +22 -0
- package/cjs/ext-slist.js +22 -0
- package/cjs/ext-value-list.js +22 -0
- package/cjs/ext-value-slist.js +22 -0
- package/cjs/{MinHeap.js → heap/min-heap.js} +77 -10
- package/cjs/heap.js +22 -0
- package/cjs/list/basics.js +88 -0
- package/cjs/list/core.js +305 -0
- package/cjs/list/ext-value.js +89 -0
- package/cjs/list/ext.js +356 -0
- package/cjs/list/nodes.js +240 -0
- package/cjs/list/ptr.js +61 -0
- package/cjs/list/value.js +100 -0
- package/cjs/list-helpers.js +91 -0
- package/cjs/list-utils.js +141 -0
- package/cjs/list.js +22 -0
- package/cjs/meta-utils.js +167 -0
- package/cjs/nt-utils.js +132 -0
- package/cjs/queue.js +58 -0
- package/cjs/slist/basics.js +71 -0
- package/cjs/slist/core.js +362 -0
- package/cjs/slist/ext-value.js +83 -0
- package/cjs/slist/ext.js +336 -0
- package/cjs/slist/nodes.js +276 -0
- package/cjs/slist/ptr.js +87 -0
- package/cjs/slist/value.js +91 -0
- package/cjs/slist.js +22 -0
- package/cjs/stack.js +55 -0
- package/cjs/value-list.js +22 -0
- package/cjs/value-slist.js +22 -0
- package/package.json +10 -8
- package/src/cache/cache-fifo.js +27 -0
- package/src/cache/cache-lfu.js +63 -0
- package/src/cache/cache-lru.js +87 -0
- package/src/cache/cache-random.js +73 -0
- package/src/cache/decorator.js +45 -0
- package/src/cache.js +9 -0
- package/src/ext-list.js +6 -0
- package/src/ext-slist.js +6 -0
- package/src/ext-value-list.js +6 -0
- package/src/ext-value-slist.js +6 -0
- package/src/{MinHeap.js → heap/min-heap.js} +91 -9
- package/src/heap.js +6 -0
- package/src/list/basics.js +64 -0
- package/src/list/core.js +314 -0
- package/src/list/ext-value.js +81 -0
- package/src/list/ext.js +370 -0
- package/src/list/nodes.js +262 -0
- package/src/list/ptr.js +58 -0
- package/src/list/value.js +88 -0
- package/src/list-helpers.js +80 -0
- package/src/list-utils.js +140 -0
- package/src/list.js +6 -0
- package/src/meta-utils.js +147 -0
- package/src/nt-utils.js +85 -0
- package/src/queue.js +52 -0
- package/src/slist/basics.js +47 -0
- package/src/slist/core.js +364 -0
- package/src/slist/ext-value.js +74 -0
- package/src/slist/ext.js +331 -0
- package/src/slist/nodes.js +290 -0
- package/src/slist/ptr.js +77 -0
- package/src/slist/value.js +75 -0
- package/src/slist.js +6 -0
- package/src/stack.js +52 -0
- package/src/value-list.js +6 -0
- package/src/value-slist.js +6 -0
- package/cjs/Cache.js +0 -70
- package/cjs/List.js +0 -291
- package/cjs/ListHead.js +0 -308
- package/cjs/SList.js +0 -336
- package/cjs/SListHead.js +0 -363
- package/cjs/utils.js +0 -43
- package/src/Cache.js +0 -61
- package/src/List.js +0 -303
- package/src/ListHead.js +0 -304
- package/src/SList.js +0 -329
- package/src/SListHead.js +0 -353
- package/src/utils.js +0 -35
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
import {copyOptions} from '
|
|
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
|
-
|
|
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
|
-
|
|
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,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
|
+
};
|
package/src/list/core.js
ADDED
|
@@ -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;
|