list-toolkit 1.0.2 → 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 +88 -419
- 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} +47 -31
- 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 +6 -6
- 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} +53 -34
- 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 -71
- package/cjs/List.js +0 -294
- package/cjs/ListHead.js +0 -309
- package/cjs/SList.js +0 -342
- package/cjs/SListHead.js +0 -367
- 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 -330
- package/src/SListHead.js +0 -354
- package/src/utils.js +0 -35
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import List, {Ptr} from './core.js';
|
|
4
|
+
import {ValueNode} from './nodes.js';
|
|
5
|
+
import {pop} from './basics.js';
|
|
6
|
+
import {addAliases, mapIterator, normalizeIterator} from '../meta-utils.js';
|
|
7
|
+
|
|
8
|
+
export class ValueList extends List {
|
|
9
|
+
popFront() {
|
|
10
|
+
if (!this.isEmpty) return pop(this, this[this.nextName]).extracted.value;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
popBack() {
|
|
14
|
+
if (!this.isEmpty) return pop(this, this[this.prevName]).extracted.value;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
adoptValue(value) {
|
|
18
|
+
if (value instanceof Ptr) {
|
|
19
|
+
if (!this.isCompatiblePtr(value)) throw new Error('Incompatible pointer');
|
|
20
|
+
if (value.node instanceof ValueNode) {
|
|
21
|
+
value.list = this;
|
|
22
|
+
return super.adoptNode(value);
|
|
23
|
+
}
|
|
24
|
+
return new ValueNode(value.node, this);
|
|
25
|
+
}
|
|
26
|
+
if (value instanceof ValueNode) {
|
|
27
|
+
if (!this.isNodeLike(value)) throw new Error('Incompatible node');
|
|
28
|
+
return super.adoptNode(value);
|
|
29
|
+
}
|
|
30
|
+
return new ValueNode(value, this);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// iterators
|
|
34
|
+
|
|
35
|
+
[Symbol.iterator]() {
|
|
36
|
+
let current = this[this.nextName],
|
|
37
|
+
readyToStop = this.isEmpty;
|
|
38
|
+
return normalizeIterator({
|
|
39
|
+
next: () => {
|
|
40
|
+
if (readyToStop && current === this) return {done: true};
|
|
41
|
+
readyToStop = true;
|
|
42
|
+
const value = current.value;
|
|
43
|
+
current = current[this.nextName];
|
|
44
|
+
return {value};
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getValueIterator(range) {
|
|
50
|
+
return mapIterator(this.getNodeIterator(range), node => node.value);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getReverseValueIterator(range) {
|
|
54
|
+
return mapIterator(this.getReverseNodeIterator(range), node => node.value);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// meta helpers
|
|
58
|
+
|
|
59
|
+
clone() {
|
|
60
|
+
return ValueList.from(this, this);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
make() {
|
|
64
|
+
return new ValueList(this);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
makeFrom(values) {
|
|
68
|
+
return ValueList.from(values, this);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static from(values, options) {
|
|
72
|
+
const list = new ValueList(options);
|
|
73
|
+
for (const value of values) list.pushBack(value);
|
|
74
|
+
return list;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
ValueList.Ptr = Ptr;
|
|
79
|
+
ValueList.ValueNode = ValueNode;
|
|
80
|
+
|
|
81
|
+
addAliases(ValueList.prototype, {
|
|
82
|
+
popFront: 'pop',
|
|
83
|
+
getValueIterator: 'getIterator',
|
|
84
|
+
getReverseValueIterator: 'getReverseIterator'
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
export {ValueNode, Ptr};
|
|
88
|
+
export default ValueList;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
export const normalizeNode = (list, node, PtrBase) => {
|
|
4
|
+
if (!node) return null;
|
|
5
|
+
if (node instanceof PtrBase) {
|
|
6
|
+
if (!list.isCompatible(node.list)) throw new Error('Incompatible lists');
|
|
7
|
+
node = node.node;
|
|
8
|
+
} else {
|
|
9
|
+
if (!list.isNodeLike(node)) throw new Error('Not a compatible node');
|
|
10
|
+
}
|
|
11
|
+
return node;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const isRangeLike = (listHost, range, PtrBase) => {
|
|
15
|
+
if (!range) return true;
|
|
16
|
+
|
|
17
|
+
if (range.list && !listHost.isCompatible(range.list)) return false;
|
|
18
|
+
|
|
19
|
+
let list = range.list;
|
|
20
|
+
|
|
21
|
+
if (range.from instanceof PtrBase) {
|
|
22
|
+
if (range.list) {
|
|
23
|
+
if (range.from.list !== range.list) return false;
|
|
24
|
+
} else {
|
|
25
|
+
if (!listHost.isCompatible(range.from.list)) return false;
|
|
26
|
+
list ||= range.from.list;
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
29
|
+
if (range.from && !listHost.isNodeLike(range.from)) return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (range.to instanceof PtrBase) {
|
|
33
|
+
if (list){
|
|
34
|
+
if (range.to.list !== list) return false;
|
|
35
|
+
} else {
|
|
36
|
+
if (!listHost.isCompatible(range.to.list)) return false;
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
if (range.to && !listHost.isNodeLike(range.to)) return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return true;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const normalizeRange = (listHost, range, PtrBase) => {
|
|
46
|
+
if (!range) return null;
|
|
47
|
+
if (!isRangeLike(listHost, range, PtrBase)) throw new Error('Not a compatible range');
|
|
48
|
+
let {from, to} = range;
|
|
49
|
+
if (from instanceof PtrBase) from = from.node;
|
|
50
|
+
if (to instanceof PtrBase) to = to.node;
|
|
51
|
+
return {...range, from, to};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const isPtrRangeLike = (listHost, range, PtrBase) => {
|
|
55
|
+
if (!range) return true;
|
|
56
|
+
if (!(range.from instanceof PtrBase)) return false;
|
|
57
|
+
|
|
58
|
+
if (range.list) {
|
|
59
|
+
if (!listHost.isCompatible(range.list)) return false;
|
|
60
|
+
if (range.from.list !== range.list) return false;
|
|
61
|
+
} else {
|
|
62
|
+
if (!listHost.isCompatible(range.from.list)) return false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (range.to instanceof PtrBase) {
|
|
66
|
+
if (range.to.list !== (range.list || range.from.list)) return false;
|
|
67
|
+
} else {
|
|
68
|
+
if (range.to && !listHost.isNodeLike(range.to)) return false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return true;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const normalizePtrRange = (listHost, range, PtrBase) => {
|
|
75
|
+
if (!range) return null;
|
|
76
|
+
if (!isPtrRangeLike(listHost, range, PtrBase)) throw new Error('Not a compatible ptr range');
|
|
77
|
+
let {from, to} = range;
|
|
78
|
+
if (to instanceof PtrBase) to = to.node;
|
|
79
|
+
return {...range, from, to};
|
|
80
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
export const isValidList = list => {
|
|
4
|
+
let current = list;
|
|
5
|
+
do {
|
|
6
|
+
const next = current[list.nextName];
|
|
7
|
+
if (!next || next[list.prevName] !== current) return false;
|
|
8
|
+
current = next;
|
|
9
|
+
} while (current !== list);
|
|
10
|
+
return true;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const isValidSList = list => {
|
|
14
|
+
let current = list;
|
|
15
|
+
do {
|
|
16
|
+
const next = current[list.nextName];
|
|
17
|
+
if (!next) return false;
|
|
18
|
+
current = next;
|
|
19
|
+
} while (current !== list);
|
|
20
|
+
return true;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const pushValuesFront = (list, values) => {
|
|
24
|
+
for (const value of values) {
|
|
25
|
+
list.pushFront(value);
|
|
26
|
+
}
|
|
27
|
+
return list;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const pushValuesBack = (list, values) => {
|
|
31
|
+
for (const value of values) {
|
|
32
|
+
list.pushBack(value);
|
|
33
|
+
}
|
|
34
|
+
return list;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const appendValuesFront = (list, values) => {
|
|
38
|
+
if (typeof list.appendFront == 'function' && list.isCompatible(values)) {
|
|
39
|
+
list.appendFront(values);
|
|
40
|
+
return list;
|
|
41
|
+
}
|
|
42
|
+
if (!Array.isArray(values)) values = Array.from(values);
|
|
43
|
+
for (let i = values.length - 1; i >= 0; --i) {
|
|
44
|
+
list.pushFront(values[i]);
|
|
45
|
+
}
|
|
46
|
+
return list;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const appendValuesBack = (list, values) => {
|
|
50
|
+
if (typeof list.appendBack == 'function' && list.isCompatible(values)) {
|
|
51
|
+
list.appendBack(values);
|
|
52
|
+
return list;
|
|
53
|
+
}
|
|
54
|
+
return pushValuesBack(list, values);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const addValuesBefore = (ptr, values) => {
|
|
58
|
+
for (const value of values) {
|
|
59
|
+
ptr.addBefore(value);
|
|
60
|
+
}
|
|
61
|
+
return ptr;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const addValuesAfter = (ptr, values) => {
|
|
65
|
+
for (const value of values) {
|
|
66
|
+
ptr.addAfter(value);
|
|
67
|
+
}
|
|
68
|
+
return ptr;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const insertValuesBefore = (ptr, values) => {
|
|
72
|
+
if (typeof ptr.insertBefore == 'function' && ptr.list.isCompatible(values)) {
|
|
73
|
+
ptr.insertBefore(ptr.list.makeFrom(values));
|
|
74
|
+
return ptr;
|
|
75
|
+
}
|
|
76
|
+
return addValuesBefore(ptr, values);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const insertValuesAfter = (ptr, values) => {
|
|
80
|
+
if (typeof ptr.insertAfter == 'function' && ptr.list.isCompatible(values)) {
|
|
81
|
+
ptr.insertAfter(ptr.list.makeFrom(values));
|
|
82
|
+
return ptr;
|
|
83
|
+
}
|
|
84
|
+
if (!Array.isArray(values)) values = Array.from(values);
|
|
85
|
+
for (let i = values.length - 1; i >= 0; --i) {
|
|
86
|
+
ptr.addAfter(values[i]);
|
|
87
|
+
}
|
|
88
|
+
return ptr;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const findNodeBy = (list, condition) => {
|
|
92
|
+
for (const node of list.getNodeIterator()) {
|
|
93
|
+
if (condition(node)) return node;
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const findPtrBy = (list, condition) => {
|
|
99
|
+
for (const ptr of list.getPtrIterator()) {
|
|
100
|
+
if (condition(ptr.node)) return ptr;
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const removeNodeBy = (list, condition) => {
|
|
106
|
+
for (const ptr of list.getPtrIterator()) {
|
|
107
|
+
if (condition(ptr.node)) return ptr.removeCurrent();
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const backPusher = (ExtListClass, options) => {
|
|
113
|
+
const list = new ExtListClass(null, options),
|
|
114
|
+
adapter = {
|
|
115
|
+
nextName: list.nextName,
|
|
116
|
+
prevName: list.prevName,
|
|
117
|
+
|
|
118
|
+
pushBackNode: node => {
|
|
119
|
+
const ptr = list.addNodeAfter(node);
|
|
120
|
+
list.next();
|
|
121
|
+
return ptr.node;
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
releaseList: () => list.make(list.next().detach())
|
|
125
|
+
};
|
|
126
|
+
return adapter;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export const frontPusher = (ExtListClass, options) => {
|
|
130
|
+
const list = new ExtListClass(null, options),
|
|
131
|
+
adapter = {
|
|
132
|
+
nextName: list.nextName,
|
|
133
|
+
prevName: list.prevName,
|
|
134
|
+
|
|
135
|
+
pushFrontNode: node => list.addNodeAfter(node).node,
|
|
136
|
+
|
|
137
|
+
releaseList: () => this.make(list.detach())
|
|
138
|
+
};
|
|
139
|
+
return adapter;
|
|
140
|
+
};
|
package/src/list.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
export const capitalize = name => (name ? name[0].toUpperCase() + name.substring(1).toLowerCase() : name);
|
|
2
|
+
|
|
3
|
+
export const toCamelCase = names => names.map((name, index) => (index ? capitalize(name) : name.toLowerCase())).join('');
|
|
4
|
+
export const fromCamelCase = name => name.split(/(?=[A-Z])/g);
|
|
5
|
+
|
|
6
|
+
export const toPascalCase = names => names.map(name => capitalize(name)).join('');
|
|
7
|
+
export const fromPascalCase = name => name.split(/(?=[A-Z])/g);
|
|
8
|
+
|
|
9
|
+
export const toAllCapsSnakeCase = names => names.map(name => name.toUpperCase()).join('_');
|
|
10
|
+
export const toSnakeCase = names => names.map(name => name.toLowerCase()).join('_');
|
|
11
|
+
export const fromSnakeCase = name => name.split('_');
|
|
12
|
+
|
|
13
|
+
export const toKebabCase = names => names.map(name => name.toLowerCase()).join('-');
|
|
14
|
+
export const fromKebabCase = name => name.split('-');
|
|
15
|
+
|
|
16
|
+
export const defaultDescriptor = {configurable: true, enumerable: true};
|
|
17
|
+
|
|
18
|
+
export const fromGetter = (getter, defaultDescriptor = defaultDescriptor) => {
|
|
19
|
+
const descriptor = {...defaultDescriptor};
|
|
20
|
+
if (typeof getter == 'function') descriptor.get = getter;
|
|
21
|
+
return descriptor;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const fromSetter = (setter, defaultDescriptor = defaultDescriptor) => {
|
|
25
|
+
const descriptor = {...defaultDescriptor};
|
|
26
|
+
if (typeof setter == 'function') descriptor.set = setter;
|
|
27
|
+
return descriptor;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const fromAccessors = (getter, setter, defaultDescriptor = defaultDescriptor) => {
|
|
31
|
+
const descriptor = {...defaultDescriptor};
|
|
32
|
+
if (typeof getter == 'function') descriptor.get = getter;
|
|
33
|
+
if (typeof setter == 'function') descriptor.set = setter;
|
|
34
|
+
return descriptor;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const addDescriptor = (target, names, descriptor, force) => {
|
|
38
|
+
if (!descriptor) return target;
|
|
39
|
+
if (typeof names == 'string') names = names.trim().split(/\s*,\s*/);
|
|
40
|
+
if (!Array.isArray(names)) names = [names];
|
|
41
|
+
for (const name of names) {
|
|
42
|
+
if (!force && target.hasOwnProperty(name)) continue;
|
|
43
|
+
Object.defineProperty(target, name, descriptor);
|
|
44
|
+
}
|
|
45
|
+
return target;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const addDescriptors = (target, dict, force) => {
|
|
49
|
+
for (const [names, descriptor] of Object.entries(dict)) {
|
|
50
|
+
addDescriptor(target, names, descriptor, force);
|
|
51
|
+
}
|
|
52
|
+
for (const symbol of Object.getOwnPropertySymbols(dict)) {
|
|
53
|
+
const descriptor = Object.getOwnPropertyDescriptor(dict, symbol);
|
|
54
|
+
if (!descriptor || !descriptor.enumerable) continue;
|
|
55
|
+
addDescriptor(target, [symbol], dict[symbol], force);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const addAccessor = (target, names, getter, setter, force) => addDescriptor(target, names, fromAccessors(getter, setter), force);
|
|
60
|
+
|
|
61
|
+
export const addGetters = (target, dict, force) => {
|
|
62
|
+
for (const [names, getter] of Object.entries(dict)) {
|
|
63
|
+
addDescriptor(target, names, fromGetter(getter), force);
|
|
64
|
+
}
|
|
65
|
+
for (const symbol of Object.getOwnPropertySymbols(dict)) {
|
|
66
|
+
const descriptor = Object.getOwnPropertyDescriptor(source, symbol);
|
|
67
|
+
if (!descriptor || !descriptor.enumerable) continue;
|
|
68
|
+
addDescriptor(target, [symbol], fromGetter(dict[symbol]), force);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const copyDescriptors = (target, source, names, force) => {
|
|
73
|
+
switch (typeof names) {
|
|
74
|
+
case 'string':
|
|
75
|
+
names = names.trim().split(/\s*,\s*/);
|
|
76
|
+
break;
|
|
77
|
+
case 'symbol':
|
|
78
|
+
names = [names];
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
if (Array.isArray(names)) {
|
|
82
|
+
for (const name of names) {
|
|
83
|
+
addDescriptor(target, [name], Object.getOwnPropertyDescriptor(source, name), force);
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
for (const [name, aliases] of Object.entries(names)) {
|
|
87
|
+
addDescriptor(target, aliases, Object.getOwnPropertyDescriptor(source, name), force);
|
|
88
|
+
}
|
|
89
|
+
for (const symbol of Object.getOwnPropertySymbols(names)) {
|
|
90
|
+
const descriptor = Object.getOwnPropertyDescriptor(names, symbol);
|
|
91
|
+
if (!descriptor || !descriptor.enumerable) continue;
|
|
92
|
+
addDescriptor(target, names[symbol], Object.getOwnPropertyDescriptor(source, symbol), force);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return target;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const addAlias = (object, name, aliases, force) => addDescriptor(object, aliases, Object.getOwnPropertyDescriptor(object, name), force);
|
|
99
|
+
|
|
100
|
+
export const addAliases = (object, dict, force) => copyDescriptors(object, object, dict, force);
|
|
101
|
+
|
|
102
|
+
export const augmentIterator = iterator => {
|
|
103
|
+
if (!Object.hasOwnProperty(Symbol.iterator)) {
|
|
104
|
+
iterator[Symbol.iterator] = function () {
|
|
105
|
+
return this;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return iterator;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
let normalizeIterator = augmentIterator;
|
|
112
|
+
if (typeof globalThis.Iterator?.from == 'function') {
|
|
113
|
+
normalizeIterator = iterator => Iterator.from(iterator);
|
|
114
|
+
}
|
|
115
|
+
export {normalizeIterator};
|
|
116
|
+
|
|
117
|
+
export const mapIterator = (iterator, callbackFn) => {
|
|
118
|
+
if (typeof iterator?.map == 'function') return iterator.map(callbackFn);
|
|
119
|
+
return {
|
|
120
|
+
[Symbol.iterator]: () => {
|
|
121
|
+
const iterable = iterator[Symbol.iterator]();
|
|
122
|
+
let index = 0;
|
|
123
|
+
return normalizeIterator({
|
|
124
|
+
next: () => {
|
|
125
|
+
const result = iterable.next();
|
|
126
|
+
if (result.done) return result;
|
|
127
|
+
return {value: callbackFn(result.value, index++)};
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const copyOptions = (target, pattern, ...sources) => {
|
|
135
|
+
target = target || {};
|
|
136
|
+
const keys = Object.keys(pattern);
|
|
137
|
+
for (const key of keys) {
|
|
138
|
+
target[key] = pattern[key];
|
|
139
|
+
}
|
|
140
|
+
for (const source of sources) {
|
|
141
|
+
if (!source || typeof source !== 'object') continue;
|
|
142
|
+
for (const key of keys) {
|
|
143
|
+
if (key in source) target[key] = source[key];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return target;
|
|
147
|
+
};
|
package/src/nt-utils.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// utilities for working with null-terminated lists
|
|
4
|
+
|
|
5
|
+
export const isNTList = (head, {nextName = 'next'} = {}) => {
|
|
6
|
+
if (head === null) return true;
|
|
7
|
+
let current = head;
|
|
8
|
+
do {
|
|
9
|
+
const next = current[nextName];
|
|
10
|
+
if (next === null) return true;
|
|
11
|
+
if (!next) break;
|
|
12
|
+
current = next;
|
|
13
|
+
} while (current !== head);
|
|
14
|
+
return false;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const getNTListTail = (head, {nextName = 'next'} = {}) => {
|
|
18
|
+
if (head === null) return null;
|
|
19
|
+
let current = head;
|
|
20
|
+
do {
|
|
21
|
+
const next = current[nextName];
|
|
22
|
+
if (!next) return current;
|
|
23
|
+
current = next;
|
|
24
|
+
} while (current !== head);
|
|
25
|
+
return null;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const getNTListHead = (node, {prevName = 'prev'} = {}) => getNTListTail(node, {nextName: prevName});
|
|
29
|
+
|
|
30
|
+
export const getNTListLength = (head, {nextName = 'next'} = {}) => {
|
|
31
|
+
if (head === null) return 0;
|
|
32
|
+
let current = head;
|
|
33
|
+
let length = 1;
|
|
34
|
+
do {
|
|
35
|
+
const next = current[nextName];
|
|
36
|
+
if (!next) return length;
|
|
37
|
+
current = next;
|
|
38
|
+
++length;
|
|
39
|
+
} while (current !== head);
|
|
40
|
+
return length;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const makeListFromNTList = (node, {nextName = 'next', prevName = 'prev'} = {}) => {
|
|
44
|
+
if (node === null) return null;
|
|
45
|
+
const head = getNTListHead(node, {prevName}),
|
|
46
|
+
tail = getNTListTail(node, {nextName});
|
|
47
|
+
head[prevName] = tail;
|
|
48
|
+
tail[nextName] = head;
|
|
49
|
+
return {head, tail};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const makeSListFromNTList = (head, {nextName = 'next'} = {}) => {
|
|
53
|
+
if (head === null) return null;
|
|
54
|
+
const tail = getNTListTail(head, {nextName});
|
|
55
|
+
tail[nextName] = head;
|
|
56
|
+
return {head, tail};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const makeNTListFromList = (head, {nextName = 'next', prevName = 'prev'} = {}) => {
|
|
60
|
+
if (head === null) return null;
|
|
61
|
+
const tail = head[prevName];
|
|
62
|
+
tail[nextName] = null;
|
|
63
|
+
head[prevName] = null;
|
|
64
|
+
return {head, tail};
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export const makeNTListFromSListFast = (head, {nextName = 'next'} = {}) => {
|
|
68
|
+
if (head === null) return null;
|
|
69
|
+
const tail = head;
|
|
70
|
+
head = head[nextName];
|
|
71
|
+
tail[nextName] = null;
|
|
72
|
+
return {head, tail};
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const makeNTListFromSList = (head, {nextName = 'next'} = {}) => {
|
|
76
|
+
if (head === null) return null;
|
|
77
|
+
let tail = head;
|
|
78
|
+
for (;;) {
|
|
79
|
+
const next = tail[nextName];
|
|
80
|
+
if (next === head) break;
|
|
81
|
+
tail = next;
|
|
82
|
+
}
|
|
83
|
+
tail[nextName] = null;
|
|
84
|
+
return {head, tail};
|
|
85
|
+
};
|
package/src/queue.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import ValueList from './value-list.js';
|
|
4
|
+
import {addAliases} from './meta-utils.js';
|
|
5
|
+
import {pushValuesBack} from './list-utils.js';
|
|
6
|
+
|
|
7
|
+
export class Queue {
|
|
8
|
+
constructor(underlyingList = new ValueList()) {
|
|
9
|
+
this.list = underlyingList;
|
|
10
|
+
this.size = this.list.getLength();
|
|
11
|
+
}
|
|
12
|
+
get isEmpty() {
|
|
13
|
+
return this.list.isEmpty;
|
|
14
|
+
}
|
|
15
|
+
get top() {
|
|
16
|
+
return this.list.isEmpty ? undefined : this.list.front.value;
|
|
17
|
+
}
|
|
18
|
+
peek() {
|
|
19
|
+
return this.list.isEmpty ? undefined : this.list.front.value;
|
|
20
|
+
}
|
|
21
|
+
add(value) {
|
|
22
|
+
this.list.pushBack(value);
|
|
23
|
+
++this.size;
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
remove() {
|
|
27
|
+
if (!this.list.isEmpty) {
|
|
28
|
+
--this.size;
|
|
29
|
+
return this.list.popFront();
|
|
30
|
+
}
|
|
31
|
+
// return undefined;
|
|
32
|
+
}
|
|
33
|
+
addValues(values) {
|
|
34
|
+
pushValuesBack(this, values);
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
clear() {
|
|
38
|
+
this.list.clear();
|
|
39
|
+
this.size = 0;
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
[Symbol.iterator]() {
|
|
43
|
+
return this.list[Symbol.iterator]();
|
|
44
|
+
}
|
|
45
|
+
getReverseIterator() {
|
|
46
|
+
return this.list.getReverseIterator?.();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
addAliases(Queue.prototype, {add: 'push, pushBack, enqueue', remove: 'pop, popFront, dequeue'});
|
|
51
|
+
|
|
52
|
+
export default Queue;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// useful low-level operations on singly linked lists
|
|
4
|
+
|
|
5
|
+
export const extract = ({nextName}, {prevFrom, to = prevFrom[nextName]}) => {
|
|
6
|
+
const node = prevFrom[nextName],
|
|
7
|
+
next = to[nextName];
|
|
8
|
+
|
|
9
|
+
// exclude the range
|
|
10
|
+
prevFrom[nextName] = to[nextName];
|
|
11
|
+
|
|
12
|
+
// circle the range
|
|
13
|
+
to[nextName] = node;
|
|
14
|
+
|
|
15
|
+
return {extracted: {prevFrom: to, to}, rest: next === node ? null : next};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// pop(options, prev).node === extract(options, {prevFrom: prev}).prevFrom[options.nextName]
|
|
19
|
+
|
|
20
|
+
export const pop = ({nextName}, prev) => {
|
|
21
|
+
const node = prev[nextName],
|
|
22
|
+
next = node[nextName];
|
|
23
|
+
|
|
24
|
+
// exclude the node
|
|
25
|
+
prev[nextName] = next;
|
|
26
|
+
|
|
27
|
+
// circle the node
|
|
28
|
+
node[nextName] = node;
|
|
29
|
+
|
|
30
|
+
return {extracted: {prevFrom: node, to: node}, rest: next === node ? null : next};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const splice = ({nextName}, target, {prevFrom, to = prevFrom[nextName]}) => {
|
|
34
|
+
// form the combined head
|
|
35
|
+
const next = target[nextName];
|
|
36
|
+
target[nextName] = prevFrom[nextName];
|
|
37
|
+
|
|
38
|
+
// finish the combined tail
|
|
39
|
+
prevFrom[nextName] = to[nextName];
|
|
40
|
+
to[nextName] = next;
|
|
41
|
+
|
|
42
|
+
return target;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// append(options, target, range) === splice(options, target, extract(options, range))
|
|
46
|
+
|
|
47
|
+
export const append = splice;
|