list-toolkit 2.1.0 → 2.2.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 +4 -2
- package/cjs/heap/basics.js +63 -0
- package/cjs/heap/leftist-heap.js +124 -0
- package/cjs/heap/min-heap.js +10 -14
- package/cjs/heap/skew-heap.js +114 -0
- package/cjs/list/nodes.js +2 -2
- package/cjs/meta-utils.js +6 -2
- package/cjs/slist/nodes.js +1 -1
- package/package.json +18 -6
- package/src/heap/basics.js +58 -0
- package/src/heap/leftist-heap.js +125 -0
- package/src/heap/min-heap.js +10 -13
- package/src/heap/skew-heap.js +113 -0
- package/src/list/nodes.js +3 -3
- package/src/meta-utils.js +3 -1
- package/src/slist/nodes.js +2 -2
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ The toolkit provides the following data structures with a full set of efficientl
|
|
|
15
15
|
* Value-based lists, where a list serves as a container for external objects, and node-based lists, where a list uses custom properties on external objects to link them around.
|
|
16
16
|
* Hosted lists, which use a special head node to manage nodes, and headless lists, which point to an external list without including any headers.
|
|
17
17
|
* Heaps:
|
|
18
|
-
*
|
|
18
|
+
* Priority queues: min heap, leftist heap, skew heap.
|
|
19
19
|
* Various list-based data structures:
|
|
20
20
|
* Caches with various eviction algorithms: least recently used (LRU), least frequently used (LFU), first in first out (FIFO), and random.
|
|
21
21
|
* A decorator is provided to decorate functions, methods, and getters with a cache of your choice.
|
|
@@ -101,7 +101,7 @@ const john = new Person('John'),
|
|
|
101
101
|
|
|
102
102
|
const people = List.from([john, jane, jim, jill]);
|
|
103
103
|
|
|
104
|
-
//
|
|
104
|
+
// iterate over the list manually:
|
|
105
105
|
for (let node = people.front; node !== people; node = node[people.nextName]) {
|
|
106
106
|
console.log(node.name); // John, Jane, Jim, Jill
|
|
107
107
|
}
|
|
@@ -146,6 +146,8 @@ BSD 3-Clause "New" or "Revised" License. See the LICENSE file for details.
|
|
|
146
146
|
|
|
147
147
|
## Release History
|
|
148
148
|
|
|
149
|
+
* 2.2.0 *Added leftist and skew heaps.*
|
|
150
|
+
* 2.1.1 *Allowed functions to be used as nodes. Updated deps.*
|
|
149
151
|
* 2.1.0 *Added splay tree. Updated deps.*
|
|
150
152
|
* 2.0.0 *New major release.*
|
|
151
153
|
* 1.0.1 *Fixed exports. Added more methods to `MinHeap`.*
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.HeapBase = void 0;
|
|
7
|
+
var _metaUtils = require("../meta-utils.js");
|
|
8
|
+
const defaultLess = (a, b) => a < b;
|
|
9
|
+
const defaultEqual = (a, b) => a === b;
|
|
10
|
+
class HeapBase {
|
|
11
|
+
constructor(options) {
|
|
12
|
+
(0, _metaUtils.copyOptions)(this, HeapBase.defaults, options);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// main methods
|
|
16
|
+
get isEmpty() {
|
|
17
|
+
throw new Error('Not implemented');
|
|
18
|
+
}
|
|
19
|
+
get top() {
|
|
20
|
+
throw new Error('Not implemented');
|
|
21
|
+
}
|
|
22
|
+
peek() {
|
|
23
|
+
return this.top;
|
|
24
|
+
}
|
|
25
|
+
pop() {
|
|
26
|
+
throw new Error('Not implemented');
|
|
27
|
+
}
|
|
28
|
+
push() {
|
|
29
|
+
throw new Error('Not implemented');
|
|
30
|
+
}
|
|
31
|
+
clear() {
|
|
32
|
+
throw new Error('Not implemented');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// performance methods
|
|
36
|
+
pushPop(value) {
|
|
37
|
+
this.push(value);
|
|
38
|
+
return this.pop();
|
|
39
|
+
}
|
|
40
|
+
replaceTop(value) {
|
|
41
|
+
const z = this.pop();
|
|
42
|
+
this.push(value);
|
|
43
|
+
return z;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// helper methods
|
|
47
|
+
merge(...args) {
|
|
48
|
+
throw new Error('Not implemented');
|
|
49
|
+
}
|
|
50
|
+
clone() {
|
|
51
|
+
throw new Error('Not implemented');
|
|
52
|
+
}
|
|
53
|
+
make(...args) {
|
|
54
|
+
return new this.constructor(this, ...args);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.HeapBase = HeapBase;
|
|
58
|
+
HeapBase.defaults = {
|
|
59
|
+
less: defaultLess,
|
|
60
|
+
equal: defaultEqual,
|
|
61
|
+
compare: null
|
|
62
|
+
};
|
|
63
|
+
var _default = exports.default = HeapBase;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.LeftistHeapNode = exports.LeftistHeap = void 0;
|
|
7
|
+
var _basics = _interopRequireDefault(require("./basics.js"));
|
|
8
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
const defaultLess = (a, b) => a < b;
|
|
10
|
+
const merge = (a, b, less) => {
|
|
11
|
+
if (!a) return b;
|
|
12
|
+
if (!b) return a;
|
|
13
|
+
if (less(b.value, a.value)) [a, b] = [b, a]; // swap
|
|
14
|
+
|
|
15
|
+
a.right = merge(a.right, b, less);
|
|
16
|
+
if (!a.left) {
|
|
17
|
+
[a.left, a.right] = [a.right, a.left]; // swap
|
|
18
|
+
a.s = 1;
|
|
19
|
+
return a;
|
|
20
|
+
}
|
|
21
|
+
if (a.left.s < a.right.s) {
|
|
22
|
+
[a.left, a.right] = [a.right, a.left]; // swap
|
|
23
|
+
}
|
|
24
|
+
a.s = a.right.s + 1;
|
|
25
|
+
return a;
|
|
26
|
+
};
|
|
27
|
+
class LeftistHeapNode {
|
|
28
|
+
constructor(value) {
|
|
29
|
+
this.value = value;
|
|
30
|
+
this.right = this.left = null;
|
|
31
|
+
this.s = 1;
|
|
32
|
+
}
|
|
33
|
+
clear() {
|
|
34
|
+
this.left = this.right = null;
|
|
35
|
+
this.s = 1;
|
|
36
|
+
}
|
|
37
|
+
clone() {
|
|
38
|
+
const node = new LeftistHeapNode(this.value);
|
|
39
|
+
node.left = this.left && this.left.clone();
|
|
40
|
+
node.right = this.right && this.right.clone();
|
|
41
|
+
return node;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.LeftistHeapNode = LeftistHeapNode;
|
|
45
|
+
class LeftistHeap extends _basics.default {
|
|
46
|
+
constructor(options, ...args) {
|
|
47
|
+
super(options);
|
|
48
|
+
if (typeof this.compare == 'function') {
|
|
49
|
+
this.less = (a, b) => this.compare(a, b) < 0;
|
|
50
|
+
}
|
|
51
|
+
this.root = null;
|
|
52
|
+
this.size = 0;
|
|
53
|
+
if (args.length) this.merge(...args);
|
|
54
|
+
}
|
|
55
|
+
get isEmpty() {
|
|
56
|
+
return !this.root;
|
|
57
|
+
}
|
|
58
|
+
get length() {
|
|
59
|
+
return this.size;
|
|
60
|
+
}
|
|
61
|
+
get top() {
|
|
62
|
+
return this.root ? this.root.value : undefined;
|
|
63
|
+
}
|
|
64
|
+
peek() {
|
|
65
|
+
return this.root ? this.root.value : undefined;
|
|
66
|
+
}
|
|
67
|
+
push(value) {
|
|
68
|
+
this.root = merge(this.root, new LeftistHeapNode(value), this.less);
|
|
69
|
+
++this.size;
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
pop() {
|
|
73
|
+
if (!this.root) return;
|
|
74
|
+
const z = this.root;
|
|
75
|
+
this.root = merge(this.root.left, this.root.right, this.less);
|
|
76
|
+
--this.size;
|
|
77
|
+
return z.value;
|
|
78
|
+
}
|
|
79
|
+
pushPop(value) {
|
|
80
|
+
if (!this.root || this.less(value, this.root.value)) return value;
|
|
81
|
+
const z = this.root;
|
|
82
|
+
this.root = merge(z.left, new LeftistHeapNode(value), this.less);
|
|
83
|
+
this.root = merge(this.root, z.right, this.less);
|
|
84
|
+
return z.value;
|
|
85
|
+
}
|
|
86
|
+
replaceTop(value) {
|
|
87
|
+
if (!this.root) {
|
|
88
|
+
this.root = new LeftistHeapNode(value);
|
|
89
|
+
this.size = 1;
|
|
90
|
+
return; // undefined
|
|
91
|
+
}
|
|
92
|
+
const z = this.root;
|
|
93
|
+
this.root = merge(z.left, new LeftistHeapNode(value), this.less);
|
|
94
|
+
this.root = merge(this.root, z.right, this.less);
|
|
95
|
+
return z.value;
|
|
96
|
+
}
|
|
97
|
+
clear() {
|
|
98
|
+
this.root = null;
|
|
99
|
+
this.size = 0;
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
merge(...args) {
|
|
103
|
+
for (const other of args) {
|
|
104
|
+
this.root = merge(this.root, other.root, this.less);
|
|
105
|
+
this.size += other.size;
|
|
106
|
+
other.root = null;
|
|
107
|
+
other.size = 0;
|
|
108
|
+
}
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
clone() {
|
|
112
|
+
const heap = new LeftistHeap(this);
|
|
113
|
+
heap.root = this.root && this.root.clone();
|
|
114
|
+
heap.size = this.size;
|
|
115
|
+
return heap;
|
|
116
|
+
}
|
|
117
|
+
static from(array, options = _basics.default.defaults) {
|
|
118
|
+
const heap = new LeftistHeap(options);
|
|
119
|
+
for (const value of array) heap.push(value);
|
|
120
|
+
return heap;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
exports.LeftistHeap = LeftistHeap;
|
|
124
|
+
var _default = exports.default = LeftistHeap;
|
package/cjs/heap/min-heap.js
CHANGED
|
@@ -4,15 +4,14 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = exports.MinHeap = void 0;
|
|
7
|
-
var
|
|
7
|
+
var _basics = _interopRequireDefault(require("./basics.js"));
|
|
8
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
8
9
|
// the following functions are inlined:
|
|
9
10
|
|
|
10
11
|
// const left = i => (i << 1) + 1;
|
|
11
12
|
// const right = i => (i + 1) << 1;
|
|
12
13
|
// const parent = i => (i - 1) >> 1;
|
|
13
14
|
|
|
14
|
-
const defaultLess = (a, b) => a < b;
|
|
15
|
-
const defaultEqual = (a, b) => a === b;
|
|
16
15
|
const up = (array, i, less = defaultLess) => {
|
|
17
16
|
for (let p = i - 1 >> 1; i > 0; i = p, p = i - 1 >> 1) {
|
|
18
17
|
const iValue = array[i],
|
|
@@ -38,15 +37,15 @@ const down = (array, i, less = defaultLess, n = array.length) => {
|
|
|
38
37
|
}
|
|
39
38
|
return array;
|
|
40
39
|
};
|
|
41
|
-
class MinHeap {
|
|
40
|
+
class MinHeap extends _basics.default {
|
|
42
41
|
constructor(options, ...args) {
|
|
43
|
-
(
|
|
42
|
+
super(options);
|
|
44
43
|
if (typeof this.compare == 'function') {
|
|
45
44
|
this.less = (a, b) => this.compare(a, b) < 0;
|
|
46
45
|
this.equal = (a, b) => !this.compare(a, b);
|
|
47
46
|
}
|
|
48
47
|
this.array = [];
|
|
49
|
-
this.merge(...args);
|
|
48
|
+
if (args.length) this.merge(...args);
|
|
50
49
|
}
|
|
51
50
|
get length() {
|
|
52
51
|
return this.array.length;
|
|
@@ -191,9 +190,6 @@ class MinHeap {
|
|
|
191
190
|
})), this.less);
|
|
192
191
|
return this;
|
|
193
192
|
}
|
|
194
|
-
make(...args) {
|
|
195
|
-
return new MinHeap(this, ...args);
|
|
196
|
-
}
|
|
197
193
|
clone() {
|
|
198
194
|
const heap = new MinHeap(this);
|
|
199
195
|
heap.array = this.array.slice(0);
|
|
@@ -288,11 +284,11 @@ class MinHeap {
|
|
|
288
284
|
}
|
|
289
285
|
return heapArray;
|
|
290
286
|
}
|
|
287
|
+
static from(array, options = MinHeap.defaults) {
|
|
288
|
+
const heap = new MinHeap(options);
|
|
289
|
+
heap.array = MinHeap.build(array, heap.less);
|
|
290
|
+
return heap;
|
|
291
|
+
}
|
|
291
292
|
}
|
|
292
293
|
exports.MinHeap = MinHeap;
|
|
293
|
-
MinHeap.defaults = {
|
|
294
|
-
less: defaultLess,
|
|
295
|
-
equal: defaultEqual,
|
|
296
|
-
compare: null
|
|
297
|
-
};
|
|
298
294
|
var _default = exports.default = MinHeap;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.SkewHeapNode = exports.SkewHeap = void 0;
|
|
7
|
+
var _basics = _interopRequireDefault(require("./basics.js"));
|
|
8
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
const merge = (a, b, less) => {
|
|
10
|
+
if (!a) return b;
|
|
11
|
+
if (!b) return a;
|
|
12
|
+
if (less(b.value, a.value)) [a, b] = [b, a]; // swap
|
|
13
|
+
|
|
14
|
+
const temp = a.right;
|
|
15
|
+
a.right = a.left;
|
|
16
|
+
a.left = merge(b, temp, less);
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
class SkewHeapNode {
|
|
20
|
+
constructor(value) {
|
|
21
|
+
this.value = value;
|
|
22
|
+
this.right = this.left = null;
|
|
23
|
+
}
|
|
24
|
+
clear() {
|
|
25
|
+
this.left = this.right = null;
|
|
26
|
+
}
|
|
27
|
+
clone() {
|
|
28
|
+
const node = new SkewHeapNode(this.value);
|
|
29
|
+
node.left = this.left && this.left.clone();
|
|
30
|
+
node.right = this.right && this.right.clone();
|
|
31
|
+
return node;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.SkewHeapNode = SkewHeapNode;
|
|
35
|
+
class SkewHeap extends _basics.default {
|
|
36
|
+
constructor(options, ...args) {
|
|
37
|
+
super(options);
|
|
38
|
+
if (typeof this.compare == 'function') {
|
|
39
|
+
this.less = (a, b) => this.compare(a, b) < 0;
|
|
40
|
+
}
|
|
41
|
+
this.root = null;
|
|
42
|
+
this.size = 0;
|
|
43
|
+
if (args.length) this.merge(...args);
|
|
44
|
+
}
|
|
45
|
+
get isEmpty() {
|
|
46
|
+
return !this.root;
|
|
47
|
+
}
|
|
48
|
+
get length() {
|
|
49
|
+
return this.size;
|
|
50
|
+
}
|
|
51
|
+
get top() {
|
|
52
|
+
return this.root ? this.root.value : undefined;
|
|
53
|
+
}
|
|
54
|
+
peek() {
|
|
55
|
+
return this.root ? this.root.value : undefined;
|
|
56
|
+
}
|
|
57
|
+
push(value) {
|
|
58
|
+
this.root = merge(this.root, new SkewHeapNode(value), this.less);
|
|
59
|
+
++this.size;
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
pop() {
|
|
63
|
+
if (!this.root) return;
|
|
64
|
+
const z = this.root;
|
|
65
|
+
this.root = merge(this.root.left, this.root.right, this.less);
|
|
66
|
+
--this.size;
|
|
67
|
+
return z.value;
|
|
68
|
+
}
|
|
69
|
+
pushPop(value) {
|
|
70
|
+
if (!this.root || this.less(value, this.root.value)) return value;
|
|
71
|
+
const z = this.root;
|
|
72
|
+
this.root = merge(z.left, new SkewHeapNode(value), this.less);
|
|
73
|
+
this.root = merge(this.root, z.right, this.less);
|
|
74
|
+
return z.value;
|
|
75
|
+
}
|
|
76
|
+
replaceTop(value) {
|
|
77
|
+
if (!this.root) {
|
|
78
|
+
this.root = new SkewHeapNode(value);
|
|
79
|
+
this.size = 1;
|
|
80
|
+
return; // undefined
|
|
81
|
+
}
|
|
82
|
+
const z = this.root;
|
|
83
|
+
this.root = merge(z.left, new SkewHeapNode(value), this.less);
|
|
84
|
+
this.root = merge(this.root, z.right, this.less);
|
|
85
|
+
return z.value;
|
|
86
|
+
}
|
|
87
|
+
clear() {
|
|
88
|
+
this.root = null;
|
|
89
|
+
this.size = 0;
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
merge(...args) {
|
|
93
|
+
for (const other of args) {
|
|
94
|
+
this.root = merge(this.root, other.root, this.less);
|
|
95
|
+
this.size += other.size;
|
|
96
|
+
other.root = null;
|
|
97
|
+
other.size = 0;
|
|
98
|
+
}
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
clone() {
|
|
102
|
+
const heap = new SkewHeap(this);
|
|
103
|
+
heap.root = this.root && this.root.clone();
|
|
104
|
+
heap.size = this.size;
|
|
105
|
+
return heap;
|
|
106
|
+
}
|
|
107
|
+
static from(array, options = _basics.default.defaults) {
|
|
108
|
+
const heap = new SkewHeap(options);
|
|
109
|
+
for (const value of array) heap.push(value);
|
|
110
|
+
return heap;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
exports.SkewHeap = SkewHeap;
|
|
114
|
+
var _default = exports.default = SkewHeap;
|
package/cjs/list/nodes.js
CHANGED
|
@@ -36,9 +36,9 @@ class HeadNode extends Node {
|
|
|
36
36
|
isNodeLike(node) {
|
|
37
37
|
if (!node) return false;
|
|
38
38
|
const next = node[this.nextName];
|
|
39
|
-
if (!next || typeof next
|
|
39
|
+
if (!next || _metaUtils.canHaveProps[typeof next] !== 1) return false;
|
|
40
40
|
const prev = node[this.prevName];
|
|
41
|
-
return prev && typeof prev
|
|
41
|
+
return prev && _metaUtils.canHaveProps[typeof prev] === 1;
|
|
42
42
|
}
|
|
43
43
|
isCompatibleNames({
|
|
44
44
|
nextName,
|
package/cjs/meta-utils.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.toSnakeCase = exports.toPascalCase = exports.toKebabCase = exports.toCamelCase = exports.toAllCapsSnakeCase = exports.normalizeIterator = exports.mapIterator = exports.fromSnakeCase = exports.fromSetter = exports.fromPascalCase = exports.fromKebabCase = exports.fromGetter = exports.fromCamelCase = exports.fromAccessors = exports.defaultDescriptor = exports.copyOptions = exports.copyDescriptors = exports.capitalize = exports.augmentIterator = exports.addGetters = exports.addDescriptors = exports.addDescriptor = exports.addAliases = exports.addAlias = exports.addAccessor = void 0;
|
|
6
|
+
exports.toSnakeCase = exports.toPascalCase = exports.toKebabCase = exports.toCamelCase = exports.toAllCapsSnakeCase = exports.normalizeIterator = exports.mapIterator = exports.fromSnakeCase = exports.fromSetter = exports.fromPascalCase = exports.fromKebabCase = exports.fromGetter = exports.fromCamelCase = exports.fromAccessors = exports.defaultDescriptor = exports.copyOptions = exports.copyDescriptors = exports.capitalize = exports.canHaveProps = exports.augmentIterator = exports.addGetters = exports.addDescriptors = exports.addDescriptor = exports.addAliases = exports.addAlias = exports.addAccessor = void 0;
|
|
7
7
|
const capitalize = name => name ? name[0].toUpperCase() + name.substring(1).toLowerCase() : name;
|
|
8
8
|
exports.capitalize = capitalize;
|
|
9
9
|
const toCamelCase = names => names.map((name, index) => index ? capitalize(name) : name.toLowerCase()).join('');
|
|
@@ -150,6 +150,10 @@ const mapIterator = (iterator, callbackFn) => {
|
|
|
150
150
|
};
|
|
151
151
|
};
|
|
152
152
|
exports.mapIterator = mapIterator;
|
|
153
|
+
const canHaveProps = exports.canHaveProps = {
|
|
154
|
+
object: 1,
|
|
155
|
+
function: 1
|
|
156
|
+
};
|
|
153
157
|
const copyOptions = (target, pattern, ...sources) => {
|
|
154
158
|
target = target || {};
|
|
155
159
|
const keys = Object.keys(pattern);
|
|
@@ -157,7 +161,7 @@ const copyOptions = (target, pattern, ...sources) => {
|
|
|
157
161
|
target[key] = pattern[key];
|
|
158
162
|
}
|
|
159
163
|
for (const source of sources) {
|
|
160
|
-
if (!source || typeof source !==
|
|
164
|
+
if (!source || canHaveProps[typeof source] !== 1) continue;
|
|
161
165
|
for (const key of keys) {
|
|
162
166
|
if (key in source) target[key] = source[key];
|
|
163
167
|
}
|
package/cjs/slist/nodes.js
CHANGED
|
@@ -36,7 +36,7 @@ class HeadNode extends Node {
|
|
|
36
36
|
isNodeLike(node) {
|
|
37
37
|
if (!node) return false;
|
|
38
38
|
const next = node[this.nextName];
|
|
39
|
-
return next && typeof next
|
|
39
|
+
return next && _metaUtils.canHaveProps[typeof next] === 1;
|
|
40
40
|
}
|
|
41
41
|
isCompatibleNames({
|
|
42
42
|
nextName
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "list-toolkit",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "List-based data structures to organize your objects.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -10,10 +10,14 @@
|
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
|
-
"test": "tape6 --flags FO",
|
|
14
13
|
"prepare-dist": "node scripts/prepare-dist.js",
|
|
15
14
|
"build": "babel src --out-dir cjs",
|
|
16
|
-
"prepublishOnly": "npm run prepare-dist && npm run build"
|
|
15
|
+
"prepublishOnly": "npm run prepare-dist && npm run build",
|
|
16
|
+
"test": "tape6 --flags FO",
|
|
17
|
+
"test:bun": "tape6-bun --flags FO",
|
|
18
|
+
"test:deno-original": "tape6-deno --flags FO",
|
|
19
|
+
"test:deno": "deno run -A `tape6-runner main` --flags FO",
|
|
20
|
+
"start": "tape6-server --trace"
|
|
17
21
|
},
|
|
18
22
|
"repository": {
|
|
19
23
|
"type": "git",
|
|
@@ -27,16 +31,18 @@
|
|
|
27
31
|
"data structures"
|
|
28
32
|
],
|
|
29
33
|
"author": "Eugene Lazutkin <eugene.lazutkin@gmail.com> (http://www.lazutkin.com/)",
|
|
34
|
+
"funding": "https://github.com/sponsors/uhop",
|
|
30
35
|
"license": "BSD-3-Clause",
|
|
31
36
|
"bugs": {
|
|
32
37
|
"url": "https://github.com/uhop/list-toolkit/issues"
|
|
33
38
|
},
|
|
34
39
|
"homepage": "https://github.com/uhop/list-toolkit#readme",
|
|
35
40
|
"devDependencies": {
|
|
36
|
-
"@babel/cli": "^7.
|
|
41
|
+
"@babel/cli": "^7.25.6",
|
|
37
42
|
"@babel/core": "^7.25.2",
|
|
38
43
|
"@babel/preset-env": "^7.25.4",
|
|
39
|
-
"
|
|
44
|
+
"nano-benchmark": "^1.0.3",
|
|
45
|
+
"tape-six": "^0.12.2"
|
|
40
46
|
},
|
|
41
47
|
"files": [
|
|
42
48
|
"/src",
|
|
@@ -57,6 +63,12 @@
|
|
|
57
63
|
"tape6": {
|
|
58
64
|
"tests": [
|
|
59
65
|
"/tests/test-*.*js"
|
|
60
|
-
]
|
|
66
|
+
],
|
|
67
|
+
"importmap": {
|
|
68
|
+
"imports": {
|
|
69
|
+
"tape-six": "/node_modules/tape-six/index.js",
|
|
70
|
+
"list-toolkit/": "/src/"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
61
73
|
}
|
|
62
74
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import {copyOptions} from '../meta-utils.js';
|
|
4
|
+
|
|
5
|
+
const defaultLess = (a, b) => a < b;
|
|
6
|
+
const defaultEqual = (a, b) => a === b;
|
|
7
|
+
|
|
8
|
+
export class HeapBase {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
copyOptions(this, HeapBase.defaults, options);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// main methods
|
|
14
|
+
get isEmpty() {
|
|
15
|
+
throw new Error('Not implemented');
|
|
16
|
+
}
|
|
17
|
+
get top() {
|
|
18
|
+
throw new Error('Not implemented');
|
|
19
|
+
}
|
|
20
|
+
peek() {
|
|
21
|
+
return this.top;
|
|
22
|
+
}
|
|
23
|
+
pop() {
|
|
24
|
+
throw new Error('Not implemented');
|
|
25
|
+
}
|
|
26
|
+
push() {
|
|
27
|
+
throw new Error('Not implemented');
|
|
28
|
+
}
|
|
29
|
+
clear() {
|
|
30
|
+
throw new Error('Not implemented');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// performance methods
|
|
34
|
+
pushPop(value) {
|
|
35
|
+
this.push(value);
|
|
36
|
+
return this.pop();
|
|
37
|
+
}
|
|
38
|
+
replaceTop(value) {
|
|
39
|
+
const z = this.pop();
|
|
40
|
+
this.push(value);
|
|
41
|
+
return z;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// helper methods
|
|
45
|
+
merge(...args) {
|
|
46
|
+
throw new Error('Not implemented');
|
|
47
|
+
}
|
|
48
|
+
clone() {
|
|
49
|
+
throw new Error('Not implemented');
|
|
50
|
+
}
|
|
51
|
+
make(...args) {
|
|
52
|
+
return new this.constructor(this, ...args);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
HeapBase.defaults = {less: defaultLess, equal: defaultEqual, compare: null};
|
|
57
|
+
|
|
58
|
+
export default HeapBase;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import HeapBase from './basics.js';
|
|
4
|
+
|
|
5
|
+
const defaultLess = (a, b) => a < b;
|
|
6
|
+
|
|
7
|
+
const merge = (a, b, less) => {
|
|
8
|
+
if (!a) return b;
|
|
9
|
+
if (!b) return a;
|
|
10
|
+
if (less(b.value, a.value)) [a, b] = [b, a]; // swap
|
|
11
|
+
|
|
12
|
+
a.right = merge(a.right, b, less);
|
|
13
|
+
|
|
14
|
+
if (!a.left) {
|
|
15
|
+
[a.left, a.right] = [a.right, a.left]; // swap
|
|
16
|
+
a.s = 1;
|
|
17
|
+
return a;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (a.left.s < a.right.s) {
|
|
21
|
+
[a.left, a.right] = [a.right, a.left]; // swap
|
|
22
|
+
}
|
|
23
|
+
a.s = a.right.s + 1;
|
|
24
|
+
return a;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export class LeftistHeapNode {
|
|
28
|
+
constructor(value) {
|
|
29
|
+
this.value = value;
|
|
30
|
+
this.right = this.left = null;
|
|
31
|
+
this.s = 1;
|
|
32
|
+
}
|
|
33
|
+
clear() {
|
|
34
|
+
this.left = this.right = null;
|
|
35
|
+
this.s = 1;
|
|
36
|
+
}
|
|
37
|
+
clone() {
|
|
38
|
+
const node = new LeftistHeapNode(this.value);
|
|
39
|
+
node.left = this.left && this.left.clone();
|
|
40
|
+
node.right = this.right && this.right.clone();
|
|
41
|
+
return node;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class LeftistHeap extends HeapBase {
|
|
46
|
+
constructor(options, ...args) {
|
|
47
|
+
super(options);
|
|
48
|
+
if (typeof this.compare == 'function') {
|
|
49
|
+
this.less = (a, b) => this.compare(a, b) < 0;
|
|
50
|
+
}
|
|
51
|
+
this.root = null;
|
|
52
|
+
this.size = 0;
|
|
53
|
+
if (args.length) this.merge(...args);
|
|
54
|
+
}
|
|
55
|
+
get isEmpty() {
|
|
56
|
+
return !this.root;
|
|
57
|
+
}
|
|
58
|
+
get length() {
|
|
59
|
+
return this.size;
|
|
60
|
+
}
|
|
61
|
+
get top() {
|
|
62
|
+
return this.root ? this.root.value : undefined;
|
|
63
|
+
}
|
|
64
|
+
peek() {
|
|
65
|
+
return this.root ? this.root.value : undefined;
|
|
66
|
+
}
|
|
67
|
+
push(value) {
|
|
68
|
+
this.root = merge(this.root, new LeftistHeapNode(value), this.less);
|
|
69
|
+
++this.size;
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
pop() {
|
|
73
|
+
if (!this.root) return;
|
|
74
|
+
const z = this.root;
|
|
75
|
+
this.root = merge(this.root.left, this.root.right, this.less);
|
|
76
|
+
--this.size;
|
|
77
|
+
return z.value;
|
|
78
|
+
}
|
|
79
|
+
pushPop(value) {
|
|
80
|
+
if (!this.root || this.less(value, this.root.value)) return value;
|
|
81
|
+
const z = this.root;
|
|
82
|
+
this.root = merge(z.left, new LeftistHeapNode(value), this.less);
|
|
83
|
+
this.root = merge(this.root, z.right, this.less);
|
|
84
|
+
return z.value;
|
|
85
|
+
}
|
|
86
|
+
replaceTop(value) {
|
|
87
|
+
if (!this.root) {
|
|
88
|
+
this.root = new LeftistHeapNode(value);
|
|
89
|
+
this.size = 1;
|
|
90
|
+
return; // undefined
|
|
91
|
+
}
|
|
92
|
+
const z = this.root;
|
|
93
|
+
this.root = merge(z.left, new LeftistHeapNode(value), this.less);
|
|
94
|
+
this.root = merge(this.root, z.right, this.less);
|
|
95
|
+
return z.value;
|
|
96
|
+
}
|
|
97
|
+
clear() {
|
|
98
|
+
this.root = null;
|
|
99
|
+
this.size = 0;
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
merge(...args) {
|
|
103
|
+
for (const other of args) {
|
|
104
|
+
this.root = merge(this.root, other.root, this.less);
|
|
105
|
+
this.size += other.size;
|
|
106
|
+
other.root = null;
|
|
107
|
+
other.size = 0;
|
|
108
|
+
}
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
clone() {
|
|
112
|
+
const heap = new LeftistHeap(this);
|
|
113
|
+
heap.root = this.root && this.root.clone();
|
|
114
|
+
heap.size = this.size;
|
|
115
|
+
return heap;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
static from(array, options = HeapBase.defaults) {
|
|
119
|
+
const heap = new LeftistHeap(options);
|
|
120
|
+
for (const value of array) heap.push(value);
|
|
121
|
+
return heap;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export default LeftistHeap;
|
package/src/heap/min-heap.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import HeapBase from './basics.js';
|
|
4
4
|
|
|
5
5
|
// the following functions are inlined:
|
|
6
6
|
|
|
@@ -8,9 +8,6 @@ import {copyOptions} from '../meta-utils.js';
|
|
|
8
8
|
// const right = i => (i + 1) << 1;
|
|
9
9
|
// const parent = i => (i - 1) >> 1;
|
|
10
10
|
|
|
11
|
-
const defaultLess = (a, b) => a < b;
|
|
12
|
-
const defaultEqual = (a, b) => a === b;
|
|
13
|
-
|
|
14
11
|
const up = (array, i, less = defaultLess) => {
|
|
15
12
|
for (let p = (i - 1) >> 1; i > 0; i = p, p = (i - 1) >> 1) {
|
|
16
13
|
const iValue = array[i],
|
|
@@ -38,15 +35,15 @@ const down = (array, i, less = defaultLess, n = array.length) => {
|
|
|
38
35
|
return array;
|
|
39
36
|
};
|
|
40
37
|
|
|
41
|
-
export class MinHeap {
|
|
38
|
+
export class MinHeap extends HeapBase {
|
|
42
39
|
constructor(options, ...args) {
|
|
43
|
-
|
|
40
|
+
super(options);
|
|
44
41
|
if (typeof this.compare == 'function') {
|
|
45
42
|
this.less = (a, b) => this.compare(a, b) < 0;
|
|
46
43
|
this.equal = (a, b) => !this.compare(a, b);
|
|
47
44
|
}
|
|
48
45
|
this.array = [];
|
|
49
|
-
this.merge(...args);
|
|
46
|
+
if (args.length) this.merge(...args);
|
|
50
47
|
}
|
|
51
48
|
|
|
52
49
|
get length() {
|
|
@@ -216,10 +213,6 @@ export class MinHeap {
|
|
|
216
213
|
return this;
|
|
217
214
|
}
|
|
218
215
|
|
|
219
|
-
make(...args) {
|
|
220
|
-
return new MinHeap(this, ...args);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
216
|
clone() {
|
|
224
217
|
const heap = new MinHeap(this);
|
|
225
218
|
heap.array = this.array.slice(0);
|
|
@@ -327,8 +320,12 @@ export class MinHeap {
|
|
|
327
320
|
}
|
|
328
321
|
return heapArray;
|
|
329
322
|
}
|
|
330
|
-
}
|
|
331
323
|
|
|
332
|
-
|
|
324
|
+
static from(array, options = MinHeap.defaults) {
|
|
325
|
+
const heap = new MinHeap(options);
|
|
326
|
+
heap.array = MinHeap.build(array, heap.less);
|
|
327
|
+
return heap;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
333
330
|
|
|
334
331
|
export default MinHeap;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import HeapBase from './basics.js';
|
|
4
|
+
|
|
5
|
+
const merge = (a, b, less) => {
|
|
6
|
+
if (!a) return b;
|
|
7
|
+
if (!b) return a;
|
|
8
|
+
if (less(b.value, a.value)) [a, b] = [b, a]; // swap
|
|
9
|
+
|
|
10
|
+
const temp = a.right;
|
|
11
|
+
a.right = a.left;
|
|
12
|
+
a.left = merge(b, temp, less);
|
|
13
|
+
|
|
14
|
+
return a;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export class SkewHeapNode {
|
|
18
|
+
constructor(value) {
|
|
19
|
+
this.value = value;
|
|
20
|
+
this.right = this.left = null;
|
|
21
|
+
}
|
|
22
|
+
clear() {
|
|
23
|
+
this.left = this.right = null;
|
|
24
|
+
}
|
|
25
|
+
clone() {
|
|
26
|
+
const node = new SkewHeapNode(this.value);
|
|
27
|
+
node.left = this.left && this.left.clone();
|
|
28
|
+
node.right = this.right && this.right.clone();
|
|
29
|
+
return node;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class SkewHeap extends HeapBase {
|
|
34
|
+
constructor(options, ...args) {
|
|
35
|
+
super(options);
|
|
36
|
+
if (typeof this.compare == 'function') {
|
|
37
|
+
this.less = (a, b) => this.compare(a, b) < 0;
|
|
38
|
+
}
|
|
39
|
+
this.root = null;
|
|
40
|
+
this.size = 0;
|
|
41
|
+
if (args.length) this.merge(...args);
|
|
42
|
+
}
|
|
43
|
+
get isEmpty() {
|
|
44
|
+
return !this.root;
|
|
45
|
+
}
|
|
46
|
+
get length() {
|
|
47
|
+
return this.size;
|
|
48
|
+
}
|
|
49
|
+
get top() {
|
|
50
|
+
return this.root ? this.root.value : undefined;
|
|
51
|
+
}
|
|
52
|
+
peek() {
|
|
53
|
+
return this.root ? this.root.value : undefined;
|
|
54
|
+
}
|
|
55
|
+
push(value) {
|
|
56
|
+
this.root = merge(this.root, new SkewHeapNode(value), this.less);
|
|
57
|
+
++this.size;
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
pop() {
|
|
61
|
+
if (!this.root) return;
|
|
62
|
+
const z = this.root;
|
|
63
|
+
this.root = merge(this.root.left, this.root.right, this.less);
|
|
64
|
+
--this.size;
|
|
65
|
+
return z.value;
|
|
66
|
+
}
|
|
67
|
+
pushPop(value) {
|
|
68
|
+
if (!this.root || this.less(value, this.root.value)) return value;
|
|
69
|
+
const z = this.root;
|
|
70
|
+
this.root = merge(z.left, new SkewHeapNode(value), this.less);
|
|
71
|
+
this.root = merge(this.root, z.right, this.less);
|
|
72
|
+
return z.value;
|
|
73
|
+
}
|
|
74
|
+
replaceTop(value) {
|
|
75
|
+
if (!this.root) {
|
|
76
|
+
this.root = new SkewHeapNode(value);
|
|
77
|
+
this.size = 1;
|
|
78
|
+
return; // undefined
|
|
79
|
+
}
|
|
80
|
+
const z = this.root;
|
|
81
|
+
this.root = merge(z.left, new SkewHeapNode(value), this.less);
|
|
82
|
+
this.root = merge(this.root, z.right, this.less);
|
|
83
|
+
return z.value;
|
|
84
|
+
}
|
|
85
|
+
clear() {
|
|
86
|
+
this.root = null;
|
|
87
|
+
this.size = 0;
|
|
88
|
+
return this;
|
|
89
|
+
}
|
|
90
|
+
merge(...args) {
|
|
91
|
+
for (const other of args) {
|
|
92
|
+
this.root = merge(this.root, other.root, this.less);
|
|
93
|
+
this.size += other.size;
|
|
94
|
+
other.root = null;
|
|
95
|
+
other.size = 0;
|
|
96
|
+
}
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
clone() {
|
|
100
|
+
const heap = new SkewHeap(this);
|
|
101
|
+
heap.root = this.root && this.root.clone();
|
|
102
|
+
heap.size = this.size;
|
|
103
|
+
return heap;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static from(array, options = HeapBase.defaults) {
|
|
107
|
+
const heap = new SkewHeap(options);
|
|
108
|
+
for (const value of array) heap.push(value);
|
|
109
|
+
return heap;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export default SkewHeap;
|
package/src/list/nodes.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
import {isRangeLike, normalizeNode, normalizeRange} from '../list-helpers.js';
|
|
4
|
-
import {addAlias, copyDescriptors} from '../meta-utils.js';
|
|
4
|
+
import {addAlias, copyDescriptors, canHaveProps} from '../meta-utils.js';
|
|
5
5
|
|
|
6
6
|
export const isNodeLike = ({nextName, prevName}, node) => node && node[prevName] && node[nextName];
|
|
7
7
|
export const isStandAlone = ({nextName, prevName}, node) => node && node[prevName] === node && node[nextName] === node;
|
|
@@ -22,9 +22,9 @@ export class HeadNode extends Node {
|
|
|
22
22
|
isNodeLike(node) {
|
|
23
23
|
if (!node) return false;
|
|
24
24
|
const next = node[this.nextName];
|
|
25
|
-
if (!next || typeof next
|
|
25
|
+
if (!next || canHaveProps[typeof next] !== 1) return false;
|
|
26
26
|
const prev = node[this.prevName];
|
|
27
|
-
return prev && typeof prev
|
|
27
|
+
return prev && canHaveProps[typeof prev] === 1;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
isCompatibleNames({nextName, prevName}) {
|
package/src/meta-utils.js
CHANGED
|
@@ -131,6 +131,8 @@ export const mapIterator = (iterator, callbackFn) => {
|
|
|
131
131
|
};
|
|
132
132
|
};
|
|
133
133
|
|
|
134
|
+
export const canHaveProps = {object: 1, function: 1};
|
|
135
|
+
|
|
134
136
|
export const copyOptions = (target, pattern, ...sources) => {
|
|
135
137
|
target = target || {};
|
|
136
138
|
const keys = Object.keys(pattern);
|
|
@@ -138,7 +140,7 @@ export const copyOptions = (target, pattern, ...sources) => {
|
|
|
138
140
|
target[key] = pattern[key];
|
|
139
141
|
}
|
|
140
142
|
for (const source of sources) {
|
|
141
|
-
if (!source || typeof source !==
|
|
143
|
+
if (!source || canHaveProps[typeof source] !== 1) continue;
|
|
142
144
|
for (const key of keys) {
|
|
143
145
|
if (key in source) target[key] = source[key];
|
|
144
146
|
}
|
package/src/slist/nodes.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
import {isRangeLike, normalizeNode, normalizeRange, normalizePtrRange} from '../list-helpers.js';
|
|
4
|
-
import {addAlias, copyDescriptors} from '../meta-utils.js';
|
|
4
|
+
import {addAlias, copyDescriptors, canHaveProps} from '../meta-utils.js';
|
|
5
5
|
|
|
6
6
|
export const isNodeLike = ({nextName}, node) => node && node[nextName];
|
|
7
7
|
export const isStandAlone = ({nextName}, node) => node && node[nextName] === node;
|
|
@@ -25,7 +25,7 @@ export class HeadNode extends Node {
|
|
|
25
25
|
isNodeLike(node) {
|
|
26
26
|
if (!node) return false;
|
|
27
27
|
const next = node[this.nextName];
|
|
28
|
-
return next && typeof next
|
|
28
|
+
return next && canHaveProps[typeof next] === 1;
|
|
29
29
|
}
|
|
30
30
|
isCompatibleNames({nextName}) {
|
|
31
31
|
return this.nextName === nextName;
|