list-toolkit 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cjs/List.js ADDED
@@ -0,0 +1,291 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ class ListNode {
8
+ constructor() {
9
+ this.prev = this.next = this;
10
+ }
11
+ }
12
+ const pop = head => {
13
+ const rest = head.next;
14
+ head.prev.next = head.next;
15
+ head.next.prev = head.prev;
16
+ head.prev = head.next = head;
17
+ return {
18
+ node: head,
19
+ list: rest
20
+ };
21
+ };
22
+ const extract = (from, to) => {
23
+ const prev = from.prev,
24
+ next = to.next;
25
+ prev.next = next;
26
+ next.prev = prev;
27
+ from.prev = to;
28
+ to.next = from;
29
+ return from;
30
+ };
31
+ const splice = (head1, head2) => {
32
+ const tail1 = head1.prev,
33
+ tail2 = head2.prev;
34
+ tail1.next = head2;
35
+ head2.prev = tail1;
36
+ tail2.next = head1;
37
+ head1.prev = tail2;
38
+ return head1;
39
+ };
40
+ class ListValueNode extends ListNode {
41
+ constructor(value) {
42
+ super();
43
+ this.value = value;
44
+ }
45
+ pop() {
46
+ return pop(this).node.value;
47
+ }
48
+ addBefore(value) {
49
+ splice(this, new ListValueNode(value));
50
+ return this;
51
+ }
52
+ addAfter(value) {
53
+ splice(this.next, new ListValueNode(value));
54
+ return this;
55
+ }
56
+ insertBefore(list) {
57
+ splice(this, pop(list).list);
58
+ return this;
59
+ }
60
+ insertAfter(list) {
61
+ splice(this.next, pop(list).list);
62
+ return this;
63
+ }
64
+ }
65
+ class List extends ListNode {
66
+ get isEmpty() {
67
+ return this.next === this;
68
+ }
69
+ get front() {
70
+ return this.next;
71
+ }
72
+ get back() {
73
+ return this.prev;
74
+ }
75
+ getLength() {
76
+ let n = 0;
77
+ for (let p = this.next; p !== this; ++n, p = p.next);
78
+ return n;
79
+ }
80
+ popFront() {
81
+ if (this.next !== this) {
82
+ return this.next.pop();
83
+ }
84
+ }
85
+ popBack() {
86
+ if (this.next !== this) {
87
+ return this.prev.pop();
88
+ }
89
+ }
90
+ pushFront(value) {
91
+ splice(this.next, new ListValueNode(value));
92
+ return this;
93
+ }
94
+ pushBack(value) {
95
+ splice(this, new ListValueNode(value));
96
+ return this;
97
+ }
98
+ appendFront(list) {
99
+ if (list.next !== list) {
100
+ splice(this.next, extract(list.next, list.prev));
101
+ }
102
+ return this;
103
+ }
104
+ appendBack(list) {
105
+ if (list.next !== list) {
106
+ splice(this, extract(list.next, list.prev));
107
+ }
108
+ return this;
109
+ }
110
+ moveToFront(node) {
111
+ if (this.next !== node) {
112
+ splice(this.next, extract(node, node));
113
+ }
114
+ return this;
115
+ }
116
+ moveToBack(node) {
117
+ if (this.prev !== node) {
118
+ splice(this, extract(node, node));
119
+ }
120
+ return this;
121
+ }
122
+ clear() {
123
+ this.prev = this.next = this;
124
+ return this;
125
+ }
126
+ remove(from, to = from) {
127
+ extract(from, to);
128
+ return this;
129
+ }
130
+ extract(from, to) {
131
+ return splice(new List(), extract(from, to));
132
+ }
133
+ reverse() {
134
+ let next = this.next;
135
+ this.next = this.prev;
136
+ this.prev = next;
137
+ while (next !== this) {
138
+ const node = next;
139
+ next = node.next;
140
+ node.next = node.prev;
141
+ node.prev = next;
142
+ }
143
+ return this;
144
+ }
145
+ sort(compareFn) {
146
+ let current = this.next;
147
+ for (const value of Array.from(this).sort(compareFn)) {
148
+ current.value = value;
149
+ current = current.next;
150
+ }
151
+ return this;
152
+ }
153
+
154
+ // iterators
155
+
156
+ [Symbol.iterator]() {
157
+ let current = this.next;
158
+ return {
159
+ next: () => {
160
+ if (current === this) return {
161
+ done: true
162
+ };
163
+ const value = current.value;
164
+ current = current.next;
165
+ return {
166
+ value
167
+ };
168
+ }
169
+ };
170
+ }
171
+ getIterable(from, to) {
172
+ return {
173
+ [Symbol.iterator]: () => {
174
+ let current = from || this.next;
175
+ const stop = to ? to.next : this;
176
+ return {
177
+ next: () => {
178
+ if (current === stop) return {
179
+ done: true
180
+ };
181
+ const value = current.value;
182
+ current = current.next;
183
+ return {
184
+ value
185
+ };
186
+ }
187
+ };
188
+ }
189
+ };
190
+ }
191
+ getNodeIterable(from, to) {
192
+ return {
193
+ [Symbol.iterator]: () => {
194
+ let current = from || this.next;
195
+ const stop = to ? to.next : this;
196
+ return {
197
+ next: () => {
198
+ if (current === stop) return {
199
+ done: true
200
+ };
201
+ const value = current;
202
+ current = current.next;
203
+ return {
204
+ value
205
+ };
206
+ }
207
+ };
208
+ }
209
+ };
210
+ }
211
+ getReverseIterable(from, to) {
212
+ return {
213
+ [Symbol.iterator]: () => {
214
+ let current = to || this.prev;
215
+ const stop = from ? from.prev : this;
216
+ return {
217
+ next: () => {
218
+ if (current === stop) return {
219
+ done: true
220
+ };
221
+ const value = current.value;
222
+ current = current.prev;
223
+ return {
224
+ value
225
+ };
226
+ }
227
+ };
228
+ }
229
+ };
230
+ }
231
+ getReverseNodeIterable(from, to) {
232
+ return {
233
+ [Symbol.iterator]: () => {
234
+ let current = to || this.prev;
235
+ const stop = from ? from.prev : this;
236
+ return {
237
+ next: () => {
238
+ if (current === stop) return {
239
+ done: true
240
+ };
241
+ const value = current;
242
+ current = current.prev;
243
+ return {
244
+ value
245
+ };
246
+ }
247
+ };
248
+ }
249
+ };
250
+ }
251
+
252
+ // helpers
253
+
254
+ makeFrom(values) {
255
+ return List.from(values);
256
+ }
257
+ pushValuesFront(values) {
258
+ for (const value of values) {
259
+ this.pushFront(value);
260
+ }
261
+ return this;
262
+ }
263
+ pushValuesBack(values) {
264
+ for (const value of values) {
265
+ this.pushBack(value);
266
+ }
267
+ return this;
268
+ }
269
+ appendValuesFront(values) {
270
+ return this.appendFront(List.from(values));
271
+ }
272
+ appendValuesBack(values) {
273
+ return this.appendBack(List.from(values));
274
+ }
275
+ static from(values) {
276
+ const list = new List();
277
+ for (const value of values) {
278
+ list.pushBack(value);
279
+ }
280
+ return list;
281
+ }
282
+ }
283
+ List.pop = pop;
284
+ List.extract = extract;
285
+ List.splice = splice;
286
+ List.Node = ListNode;
287
+ List.ValueNode = ListValueNode;
288
+ List.prototype.pop = List.prototype.popFront;
289
+ List.prototype.push = List.prototype.pushFront;
290
+ List.prototype.append = List.prototype.appendBack;
291
+ var _default = exports.default = List;
@@ -0,0 +1,308 @@
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 ListHead {
9
+ constructor(head = null, options) {
10
+ if (head instanceof ListHead) {
11
+ ({
12
+ nextName: this.nextName,
13
+ prevName: this.prevName,
14
+ head: this.head
15
+ } = head);
16
+ return;
17
+ }
18
+ (0, _utils.copyOptions)(this, ListHead.defaults, options);
19
+ if (head instanceof ListHead.Unsafe) {
20
+ this.head = head.head;
21
+ return;
22
+ }
23
+ if (head) {
24
+ switch ((head[this.nextName] ? 2 : 0) + (head[this.prevName] ? 1 : 0)) {
25
+ case 0:
26
+ this.adopt(head);
27
+ break;
28
+ case 3:
29
+ // do nothing
30
+ break;
31
+ default:
32
+ throw new Error(`head is not an empty object nor a list with required properties (${this.nextName}, ${this.prevName})`);
33
+ }
34
+ } else {
35
+ head = this.makeNode();
36
+ }
37
+ this.head = head;
38
+ }
39
+ get isEmpty() {
40
+ return this.head[this.nextName] === this.head;
41
+ }
42
+ get front() {
43
+ return this.head[this.nextName];
44
+ }
45
+ get back() {
46
+ return this.head[this.prevName];
47
+ }
48
+ getLength() {
49
+ let n = 0;
50
+ for (let p = this.head[this.nextName]; p !== this.head; ++n, p = p[this.nextName]);
51
+ return n;
52
+ }
53
+ popFront() {
54
+ if (this.head[this.prevName] !== this.head) {
55
+ return ListHead.pop(this, this.head[this.nextName]).node;
56
+ }
57
+ }
58
+ popBack() {
59
+ if (this.head[this.prevName] !== this.head) {
60
+ return ListHead.pop(this, this.head[this.prevName]).node;
61
+ }
62
+ }
63
+ pushFront(node) {
64
+ this.adopt(node);
65
+ ListHead.splice(this, this.head[this.nextName], node);
66
+ return this;
67
+ }
68
+ pushBack(node) {
69
+ this.adopt(node);
70
+ ListHead.splice(this, this.head, node);
71
+ return this;
72
+ }
73
+ appendFront(list) {
74
+ if (list instanceof ListHead) {
75
+ list = list.head;
76
+ }
77
+ if (list[this.prevName] !== list) {
78
+ ListHead.splice(this, this.head[this.nextName], ListHead.extract(this, list[this.nextName], list[this.prevName]));
79
+ }
80
+ return this;
81
+ }
82
+ appendBack(list) {
83
+ if (list instanceof ListHead) {
84
+ list = list.head;
85
+ }
86
+ if (list[this.prevName] !== list) {
87
+ ListHead.splice(this, this.head, ListHead.extract(this, list[this.nextName], list[this.prevName]));
88
+ }
89
+ return this;
90
+ }
91
+ moveToFront(node) {
92
+ if (this.head[this.nextName] !== node) {
93
+ ListHead.splice(this, this.head[this.nextName], ListHead.extract(this, node, node));
94
+ }
95
+ return this;
96
+ }
97
+ moveToBack(node) {
98
+ if (this.head[this.prevName] !== node) {
99
+ ListHead.splice(this, this.head, ListHead.extract(this, node, node));
100
+ }
101
+ return this;
102
+ }
103
+ clear(drop) {
104
+ if (drop) {
105
+ while (!this.isEmpty) this.popFront();
106
+ } else {
107
+ this.head[this.prevName] = this.head[this.nextName] = this.head;
108
+ }
109
+ return this;
110
+ }
111
+ remove(from, to = from) {
112
+ ListHead.extract(this, from, to);
113
+ return this;
114
+ }
115
+ extract(from, to) {
116
+ return this.make(ListHead.splice(this, this.makeNode(), ListHead.extract(this, from, to)));
117
+ }
118
+ reverse() {
119
+ const list = this.head;
120
+ let next = list[this.nextName];
121
+ list[this.nextName] = list[this.prevName];
122
+ list[this.prevName] = next;
123
+ while (next !== list) {
124
+ const node = next;
125
+ next = node[this.nextName];
126
+ node[this.nextName] = node[this.prevName];
127
+ node[this.prevName] = next;
128
+ }
129
+ return this;
130
+ }
131
+ sort(compareFn) {
132
+ let prev = this.head;
133
+ for (const node of Array.from(this).sort(compareFn)) {
134
+ prev[this.nextName] = node;
135
+ node[this.prevName] = prev;
136
+ prev = node;
137
+ }
138
+ this.head[this.prevName] = prev;
139
+ prev[this.nextName] = this.head;
140
+ return this;
141
+ }
142
+
143
+ // iterators
144
+
145
+ [Symbol.iterator]() {
146
+ let current = this.head[this.nextName];
147
+ return {
148
+ next: () => {
149
+ if (current === this.head) return {
150
+ done: true
151
+ };
152
+ const value = current;
153
+ current = current[this.nextName];
154
+ return {
155
+ value
156
+ };
157
+ }
158
+ };
159
+ }
160
+ getIterable(from, to) {
161
+ return {
162
+ [Symbol.iterator]: () => {
163
+ let current = from || this.head[this.nextName];
164
+ const stop = to ? to[this.nextName] : this.head;
165
+ return {
166
+ next: () => {
167
+ if (current === stop) return {
168
+ done: true
169
+ };
170
+ const value = current;
171
+ current = current[this.nextName];
172
+ return {
173
+ value
174
+ };
175
+ }
176
+ };
177
+ }
178
+ };
179
+ }
180
+ getReverseIterable(from, to) {
181
+ return {
182
+ [Symbol.iterator]: () => {
183
+ let current = to || this.head[this.prevName];
184
+ const stop = from ? from[this.prevName] : this.head;
185
+ return {
186
+ next: () => {
187
+ if (current === stop) return {
188
+ done: true
189
+ };
190
+ const value = current;
191
+ current = current[this.prevName];
192
+ return {
193
+ value
194
+ };
195
+ }
196
+ };
197
+ }
198
+ };
199
+ }
200
+
201
+ // static utilities
202
+
203
+ static pop({
204
+ nextName,
205
+ prevName
206
+ }, node) {
207
+ const list = node[nextName];
208
+ // the next line stitches the rest of the list excluding the node, and collapse the node into a one-node list
209
+ node[prevName] = node[nextName] = ({
210
+ [nextName]: node[prevName][nextName],
211
+ [prevName]: node[nextName][prevName]
212
+ } = node);
213
+ return {
214
+ node,
215
+ list
216
+ };
217
+ }
218
+ static extract({
219
+ nextName,
220
+ prevName
221
+ }, from, to) {
222
+ const prev = from[prevName],
223
+ next = to[nextName];
224
+ prev[nextName] = next;
225
+ next[prevName] = prev;
226
+ from[prevName] = to;
227
+ to[nextName] = from;
228
+ return from;
229
+ }
230
+ static splice({
231
+ nextName,
232
+ prevName
233
+ }, list1, list2) {
234
+ const tail1 = list1[prevName],
235
+ tail2 = list2[prevName];
236
+ tail1[nextName] = list2;
237
+ list2[prevName] = tail1;
238
+ tail2[nextName] = list1;
239
+ list1[prevName] = tail2;
240
+ return list1;
241
+ }
242
+
243
+ // node helpers
244
+
245
+ makeNode() {
246
+ const node = {};
247
+ node[this.nextName] = node[this.prevName] = node;
248
+ return node;
249
+ }
250
+ adopt(node) {
251
+ if (node[this.nextName] || node[this.prevName]) {
252
+ if (node[this.nextName] === node && node[this.prevName] === node) return node;
253
+ throw new Error('node is already a part of a list, or there is a name clash');
254
+ }
255
+ node[this.nextName] = node[this.prevName] = node;
256
+ return node;
257
+ }
258
+
259
+ // helpers
260
+
261
+ clone() {
262
+ return new ListHead(this);
263
+ }
264
+ make(newHead = null) {
265
+ return new ListHead(newHead, this);
266
+ }
267
+ makeFrom(values) {
268
+ return ListHead.from(values, this);
269
+ }
270
+ pushValuesFront(values) {
271
+ for (const value of values) {
272
+ this.pushFront(value);
273
+ }
274
+ return this;
275
+ }
276
+ pushValuesBack(values) {
277
+ for (const value of values) {
278
+ this.pushBack(value);
279
+ }
280
+ return this;
281
+ }
282
+ appendValuesFront(values) {
283
+ return this.appendFront(ListHead.from(values, this));
284
+ }
285
+ appendValuesBack(values) {
286
+ return this.appendBack(ListHead.from(values, this));
287
+ }
288
+ static from(values, options) {
289
+ const list = new ListHead(null, options);
290
+ for (const value of values) {
291
+ list.pushBack(value);
292
+ }
293
+ return list;
294
+ }
295
+ }
296
+ ListHead.defaults = {
297
+ nextName: 'next',
298
+ prevName: 'prev'
299
+ };
300
+ ListHead.prototype.pop = ListHead.prototype.popFront;
301
+ ListHead.prototype.push = ListHead.prototype.pushFront;
302
+ ListHead.prototype.append = ListHead.prototype.appendBack;
303
+ ListHead.Unsafe = class {
304
+ constructor(head) {
305
+ this.head = head;
306
+ }
307
+ };
308
+ var _default = exports.default = ListHead;