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 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
- * Min-heap to support priority queues.
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
- // iterator over the list manually:
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;
@@ -4,15 +4,14 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = exports.MinHeap = void 0;
7
- var _metaUtils = require("../meta-utils.js");
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
- (0, _metaUtils.copyOptions)(this, MinHeap.defaults, options);
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 != 'object') return false;
39
+ if (!next || _metaUtils.canHaveProps[typeof next] !== 1) return false;
40
40
  const prev = node[this.prevName];
41
- return prev && typeof prev == 'object';
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 !== 'object') continue;
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
  }
@@ -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 == 'object';
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.1.0",
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.24.8",
41
+ "@babel/cli": "^7.25.6",
37
42
  "@babel/core": "^7.25.2",
38
43
  "@babel/preset-env": "^7.25.4",
39
- "tape-six": "^0.9.6"
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;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- import {copyOptions} from '../meta-utils.js';
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
- copyOptions(this, MinHeap.defaults, options);
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
- MinHeap.defaults = {less: defaultLess, equal: defaultEqual, compare: null};
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 != 'object') return false;
25
+ if (!next || canHaveProps[typeof next] !== 1) return false;
26
26
  const prev = node[this.prevName];
27
- return prev && typeof prev == 'object';
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 !== 'object') continue;
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
  }
@@ -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 == 'object';
28
+ return next && canHaveProps[typeof next] === 1;
29
29
  }
30
30
  isCompatibleNames({nextName}) {
31
31
  return this.nextName === nextName;