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