list-toolkit 1.0.1
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/LICENSE +11 -0
- package/README.md +465 -0
- package/cjs/Cache.js +70 -0
- package/cjs/List.js +291 -0
- package/cjs/ListHead.js +308 -0
- package/cjs/MinHeap.js +261 -0
- package/cjs/SList.js +336 -0
- package/cjs/SListHead.js +363 -0
- package/cjs/package.json +1 -0
- package/cjs/utils.js +43 -0
- package/package.json +60 -0
- package/src/Cache.js +61 -0
- package/src/List.js +303 -0
- package/src/ListHead.js +304 -0
- package/src/MinHeap.js +283 -0
- package/src/SList.js +329 -0
- package/src/SListHead.js +353 -0
- package/src/utils.js +35 -0
package/src/SListHead.js
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import {copyOptions} from './utils.js';
|
|
4
|
+
|
|
5
|
+
class SListHead {
|
|
6
|
+
constructor(head = null, options) {
|
|
7
|
+
if (head instanceof SListHead) {
|
|
8
|
+
({nextName: this.nextName, head: this.head} = head);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
copyOptions(this, SListHead.defaults, options);
|
|
12
|
+
if (head instanceof SListHead.Unsafe) {
|
|
13
|
+
this.head = head.head;
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (head) {
|
|
17
|
+
if (!head[this.nextName]) {
|
|
18
|
+
this.adopt(head);
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
head = this.makeNode();
|
|
22
|
+
}
|
|
23
|
+
this.head = head;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get isEmpty() {
|
|
27
|
+
return this.head[this.nextName] === this.head;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get front() {
|
|
31
|
+
return this.head[this.nextName];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getBack() {
|
|
35
|
+
return SListHead.getPrev(this, this.head);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getLength() {
|
|
39
|
+
let n = 0;
|
|
40
|
+
for (let p = this.head[this.nextName]; p !== this.head; ++n, p = p[this.nextName]);
|
|
41
|
+
return n;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getPtr() {
|
|
45
|
+
return new SListHead.SListPtr(this);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
popFront() {
|
|
49
|
+
if (this.head[this.nextName] !== this.head) {
|
|
50
|
+
return SListHead.pop(this, this.head).node;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
popBack() {
|
|
55
|
+
if (this.head[this.nextName] !== this.head) {
|
|
56
|
+
const prevLast = SListHead.getPrevPrev(this, this.head);
|
|
57
|
+
return SListHead.pop(this, prevLast).node;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
pushFront(node) {
|
|
62
|
+
this.adopt(node);
|
|
63
|
+
SListHead.splice(this, this.head, {prev: node, node});
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
pushBack(node) {
|
|
68
|
+
this.adopt(node);
|
|
69
|
+
const last = SListHead.getPrev(this, this.head);
|
|
70
|
+
SListHead.splice(this, last, {prev: node, node});
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
appendFront(list) {
|
|
75
|
+
let prevFrom, nodeTo;
|
|
76
|
+
if (list instanceof SListHead) {
|
|
77
|
+
if (list.head[this.nextName] === list.head) return this;
|
|
78
|
+
prevFrom = list.head;
|
|
79
|
+
nodeTo = SListHead.getPrev(this, list.head);
|
|
80
|
+
} else {
|
|
81
|
+
if (list[this.nextName] === list) return this;
|
|
82
|
+
prevFrom = nodeTo = SListHead.getPrev(this, list);
|
|
83
|
+
}
|
|
84
|
+
SListHead.splice(this, this.head, SListHead.extract(this, prevFrom, nodeTo));
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
appendBack(list) {
|
|
89
|
+
let prevFrom, nodeTo;
|
|
90
|
+
if (list instanceof SListHead) {
|
|
91
|
+
if (list.head[this.nextName] === list.head) return this;
|
|
92
|
+
prevFrom = list.head;
|
|
93
|
+
nodeTo = SListHead.getPrev(this, list.head);
|
|
94
|
+
} else {
|
|
95
|
+
if (list[this.nextName] === list) return this;
|
|
96
|
+
prevFrom = nodeTo = SListHead.getPrev(this, list);
|
|
97
|
+
}
|
|
98
|
+
const last = SListHead.getPrev(this, this.head);
|
|
99
|
+
SListHead.splice(this, last, SListHead.extract(this, prevFrom, nodeTo));
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
moveToFront(node) {
|
|
104
|
+
let prev;
|
|
105
|
+
if (node instanceof SListHead.SListPtr) {
|
|
106
|
+
prev = node.prev;
|
|
107
|
+
node = node.node;
|
|
108
|
+
if (this.head[this.nextName] === node) return this;
|
|
109
|
+
} else {
|
|
110
|
+
if (this.head[this.nextName] === node) return this;
|
|
111
|
+
prev = SListHead.getPrev(this, this.head, node);
|
|
112
|
+
}
|
|
113
|
+
SListHead.splice(this, this.head, SListHead.extract(this, prev, node));
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
moveToBack(node) {
|
|
118
|
+
let prev;
|
|
119
|
+
if (node instanceof SListHead.SListPtr) {
|
|
120
|
+
prev = node.prev;
|
|
121
|
+
node = node.node;
|
|
122
|
+
} else {
|
|
123
|
+
prev = SListHead.getPrev(this, this.head, node);
|
|
124
|
+
}
|
|
125
|
+
const last = SListHead.getPrev(this, this.head);
|
|
126
|
+
SListHead.splice(this, last, SListHead.extract(this, prev, node));
|
|
127
|
+
return this;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
clear(drop) {
|
|
131
|
+
if (drop) {
|
|
132
|
+
while (!this.isEmpty) this.popFront();
|
|
133
|
+
} else {
|
|
134
|
+
this.head[this.nextName] = this.head;
|
|
135
|
+
}
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
remove(from, to = from) {
|
|
140
|
+
const prevFrom = from instanceof SListHead.SListPtr ? from.prev : SListHead.getPrev(this, this.head, from),
|
|
141
|
+
nodeTo = to instanceof SListHead.SListPtr ? to.node : to;
|
|
142
|
+
SListHead.extract(this, prevFrom, nodeTo);
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
extract(from, to) {
|
|
147
|
+
const prevFrom = from instanceof SListHead.SListPtr ? from.prev : SListHead.getPrev(this, this.head, from),
|
|
148
|
+
nodeTo = to instanceof SListHead.SListPtr ? to.node : to;
|
|
149
|
+
return this.make(SListHead.splice(this, this.makeNode(), SListHead.extract(this, prevFrom, nodeTo)));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
reverse() {
|
|
153
|
+
let prev = this.head,
|
|
154
|
+
current = prev[this.nextName];
|
|
155
|
+
while (current !== this.head) {
|
|
156
|
+
const next = current[this.nextName];
|
|
157
|
+
current[this.nextName] = prev;
|
|
158
|
+
prev = current;
|
|
159
|
+
current = next;
|
|
160
|
+
}
|
|
161
|
+
this.head[this.nextName] = prev;
|
|
162
|
+
return this;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
sort(compareFn) {
|
|
166
|
+
let prev = this.head;
|
|
167
|
+
for (const node of Array.from(this).sort(compareFn)) {
|
|
168
|
+
prev = prev[this.nextName] = node;
|
|
169
|
+
}
|
|
170
|
+
prev[this.nextName] = this.head;
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// iterators
|
|
175
|
+
|
|
176
|
+
[Symbol.iterator]() {
|
|
177
|
+
let current = this.head[this.nextName];
|
|
178
|
+
return {
|
|
179
|
+
next: () => {
|
|
180
|
+
if (current === this.head) return {done: true};
|
|
181
|
+
const value = current;
|
|
182
|
+
current = current[this.nextName];
|
|
183
|
+
return {value};
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
getIterable(from, to) {
|
|
189
|
+
return {
|
|
190
|
+
[Symbol.iterator]: () => {
|
|
191
|
+
let current = (from instanceof SListHead.SListPtr && from.node) || from || this.head[this.nextName];
|
|
192
|
+
const stop = to ? ((to instanceof SListHead.SListPtr && to.node) || to)[this.nextName] : this.head;
|
|
193
|
+
return {
|
|
194
|
+
next: () => {
|
|
195
|
+
if (current === stop) return {done: true};
|
|
196
|
+
const value = current;
|
|
197
|
+
current = current[this.nextName];
|
|
198
|
+
return {value};
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
getPtrIterable(from, to) {
|
|
206
|
+
return {
|
|
207
|
+
[Symbol.iterator]: () => {
|
|
208
|
+
let current =
|
|
209
|
+
from instanceof SListHead.SListPtr ? from.clone() : from ? new SListHead.SListPtr(this, SListHead.getPrev(this, this.head, from)) : this.getPtr();
|
|
210
|
+
const stop = to ? ((to instanceof SListHead.SListPtr && to.node) || to)[this.nextName] : this.head;
|
|
211
|
+
return {
|
|
212
|
+
next: () => {
|
|
213
|
+
if (current.node === stop) return {done: true};
|
|
214
|
+
const value = current.clone();
|
|
215
|
+
current.next();
|
|
216
|
+
return {value};
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// static utilities
|
|
224
|
+
|
|
225
|
+
static pop({nextName}, prev) {
|
|
226
|
+
const node = prev[nextName];
|
|
227
|
+
prev[nextName] = node[nextName];
|
|
228
|
+
node[nextName] = node;
|
|
229
|
+
return {node, list: prev};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
static extract({nextName}, prevFrom, nodeTo) {
|
|
233
|
+
const node = prevFrom[nextName];
|
|
234
|
+
if (prevFrom === nodeTo) return {prev: prevFrom, node};
|
|
235
|
+
prevFrom[nextName] = nodeTo[nextName];
|
|
236
|
+
nodeTo[nextName] = node;
|
|
237
|
+
return {prev: nodeTo, node};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
static splice({nextName}, prev1, {prev, node}) {
|
|
241
|
+
prev[nextName] = prev1[nextName];
|
|
242
|
+
prev1[nextName] = node;
|
|
243
|
+
return prev1;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
static getPrevPrev({nextName}, list, node) {
|
|
247
|
+
let prev = list,
|
|
248
|
+
current = prev[nextName];
|
|
249
|
+
while (current[nextName] !== list) {
|
|
250
|
+
if (node && current[nextName] === node) return prev;
|
|
251
|
+
prev = current;
|
|
252
|
+
current = prev[nextName];
|
|
253
|
+
}
|
|
254
|
+
if (node) {
|
|
255
|
+
if (list[nextName] === node) return current;
|
|
256
|
+
throw new Error('node does not belong to the list');
|
|
257
|
+
}
|
|
258
|
+
return prev;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
static getPrev(names, list, node) {
|
|
262
|
+
return SListHead.getPrevPrev(names, list, node)[names.nextName];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// node helpers
|
|
266
|
+
|
|
267
|
+
makeNode() {
|
|
268
|
+
const node = {};
|
|
269
|
+
node[this.nextName] = node;
|
|
270
|
+
return node;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
adopt(node) {
|
|
274
|
+
if (node[this.nextName]) {
|
|
275
|
+
if (node[this.nextName] === node) return node;
|
|
276
|
+
throw new Error('node is already a part of a list, or there is a name clash');
|
|
277
|
+
}
|
|
278
|
+
node[this.nextName] = node;
|
|
279
|
+
return node;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// helpers
|
|
283
|
+
|
|
284
|
+
clone() {
|
|
285
|
+
return new SListHead(this);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
make(newHead = null) {
|
|
289
|
+
return new SListHead(newHead, this);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
makeFrom(values) {
|
|
293
|
+
return SListHead.from(values, this);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
pushValuesFront(values) {
|
|
297
|
+
for (const value of values) {
|
|
298
|
+
this.pushFront(value);
|
|
299
|
+
}
|
|
300
|
+
return this;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
appendValuesFront(values) {
|
|
304
|
+
return this.appendFront(SListHead.from(values, this));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
static from(values, options) {
|
|
308
|
+
const list = new SListHead(null, options),
|
|
309
|
+
nextName = list.nextName;
|
|
310
|
+
let prev = list.head;
|
|
311
|
+
for (const value of values) {
|
|
312
|
+
list.adopt(value);
|
|
313
|
+
prev[nextName] = value;
|
|
314
|
+
prev = value;
|
|
315
|
+
}
|
|
316
|
+
prev[nextName] = list.head;
|
|
317
|
+
return list;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
SListHead.defaults = {nextName: 'next'};
|
|
322
|
+
|
|
323
|
+
SListHead.prototype.pop = SListHead.prototype.popFront;
|
|
324
|
+
SListHead.prototype.push = SListHead.prototype.pushFront;
|
|
325
|
+
SListHead.prototype.append = SListHead.prototype.appendBack;
|
|
326
|
+
|
|
327
|
+
SListHead.Unsafe = class Unsafe {
|
|
328
|
+
constructor(head) {
|
|
329
|
+
this.head = head;
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
SListHead.SListPtr = class SListPtr {
|
|
334
|
+
constructor(list, prev) {
|
|
335
|
+
this.list = list;
|
|
336
|
+
this.prev = prev || list.head;
|
|
337
|
+
}
|
|
338
|
+
get node() {
|
|
339
|
+
return this.prev[this.list.nextName];
|
|
340
|
+
}
|
|
341
|
+
get isHead() {
|
|
342
|
+
return this.prev[this.list.nextName] === this.list.head;
|
|
343
|
+
}
|
|
344
|
+
next() {
|
|
345
|
+
this.prev = this.prev[this.list.nextName];
|
|
346
|
+
return this;
|
|
347
|
+
}
|
|
348
|
+
clone() {
|
|
349
|
+
return new SListHead.SListPtr(this.list, this.prev);
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
export default SListHead;
|
package/src/utils.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
export const compareFromLess = lessFn => (a, b) => lessFn(a, b) ? -1 : lessFn(b, a) ? 1 : 0;
|
|
4
|
+
export const lessFromCompare = compareFn => (a, b) => compareFn(a, b) < 0;
|
|
5
|
+
export const equalFromLess = lessFn => (a, b) => !lessFn(a, b) && !lessFn(b, a);
|
|
6
|
+
export const greaterFromLess = lessFn => (a, b) => lessFn(b, a);
|
|
7
|
+
|
|
8
|
+
export const binarySearch = (sortedArray, lessValueFn, from = 0, to = sortedArray.length) => {
|
|
9
|
+
let left = from,
|
|
10
|
+
right = to;
|
|
11
|
+
while (left < right) {
|
|
12
|
+
const mid = (left + right) >> 1;
|
|
13
|
+
if (lessValueFn(sortedArray[mid])) {
|
|
14
|
+
left = mid + 1;
|
|
15
|
+
} else {
|
|
16
|
+
right = mid;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return right;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const copyOptions = (target, pattern, ...sources) => {
|
|
23
|
+
target = target || {};
|
|
24
|
+
const keys = Object.keys(pattern);
|
|
25
|
+
for (const key of keys) {
|
|
26
|
+
target[key] = pattern[key];
|
|
27
|
+
}
|
|
28
|
+
for (const source of sources) {
|
|
29
|
+
if (!source || typeof source !== 'object') continue;
|
|
30
|
+
for (const key of keys) {
|
|
31
|
+
if (key in source) target[key] = source[key];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return target;
|
|
35
|
+
};
|