@ohos-ports/doublylinked 2.5.6-beta.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/LICENSE +21 -0
- package/README.md +549 -0
- package/lib/doubly-linked.d.ts +129 -0
- package/lib/doubly-linked.js +746 -0
- package/package.json +40 -0
|
@@ -0,0 +1,746 @@
|
|
|
1
|
+
/* doublylinked
|
|
2
|
+
------------------------
|
|
3
|
+
(c) 2017-present Panates
|
|
4
|
+
SQB may be freely distributed under the MIT license.
|
|
5
|
+
For details and documentation:
|
|
6
|
+
https://panates.github.io/doublylinked/
|
|
7
|
+
*/
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* @class
|
|
13
|
+
*/
|
|
14
|
+
class DoublyLinked {
|
|
15
|
+
/**
|
|
16
|
+
* @param {*} element... - The elements to add to the end of the list
|
|
17
|
+
* @constructor
|
|
18
|
+
*/
|
|
19
|
+
constructor(...element) {
|
|
20
|
+
this._cursor = undefined;
|
|
21
|
+
this._head = undefined;
|
|
22
|
+
this._tail = undefined;
|
|
23
|
+
this._length = 0;
|
|
24
|
+
this._eof = undefined;
|
|
25
|
+
if (element.length) {
|
|
26
|
+
this.push.apply(this, element);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
*
|
|
32
|
+
* @returns {Node}
|
|
33
|
+
*/
|
|
34
|
+
get cursor() {
|
|
35
|
+
return this._cursor;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
*
|
|
40
|
+
* @returns {Node}
|
|
41
|
+
*/
|
|
42
|
+
get head() {
|
|
43
|
+
return this._head;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
*
|
|
48
|
+
* @returns {int}
|
|
49
|
+
*/
|
|
50
|
+
get length() {
|
|
51
|
+
return this._length;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
*
|
|
56
|
+
* @returns {Node}
|
|
57
|
+
*/
|
|
58
|
+
get tail() {
|
|
59
|
+
return this._tail;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Merges cursor list with and given lists/values into new list
|
|
64
|
+
*
|
|
65
|
+
* @param {String} element... - Lists and/or values to concatenate into a new list
|
|
66
|
+
* @return {DoublyLinked} - A new DoublyLinked instance
|
|
67
|
+
* @public
|
|
68
|
+
*/
|
|
69
|
+
concat(...element) {
|
|
70
|
+
const result = new DoublyLinked();
|
|
71
|
+
const mergeFn = (acc, node) => {
|
|
72
|
+
acc.push(node);
|
|
73
|
+
return acc;
|
|
74
|
+
};
|
|
75
|
+
this.reduce(mergeFn, result);
|
|
76
|
+
for (const arg of element) {
|
|
77
|
+
if (arg instanceof DoublyLinked) {
|
|
78
|
+
arg.reduce(mergeFn, result);
|
|
79
|
+
} else result.push(arg);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return result.reset();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Returns the iterator object contains entries
|
|
87
|
+
*
|
|
88
|
+
* @return {Iterator}
|
|
89
|
+
*/
|
|
90
|
+
entries() {
|
|
91
|
+
const list = this;
|
|
92
|
+
return {
|
|
93
|
+
[Symbol.iterator]() {
|
|
94
|
+
let _cursor;
|
|
95
|
+
let i = 0;
|
|
96
|
+
return {
|
|
97
|
+
next: () => {
|
|
98
|
+
_cursor = _cursor ? _cursor.next : list.head;
|
|
99
|
+
return {
|
|
100
|
+
value: _cursor && [i++, _cursor.value],
|
|
101
|
+
done: !_cursor,
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Returns the iterator object contains keys
|
|
111
|
+
*
|
|
112
|
+
* @return {Iterator}
|
|
113
|
+
*/
|
|
114
|
+
keys() {
|
|
115
|
+
const list = this;
|
|
116
|
+
return {
|
|
117
|
+
[Symbol.iterator]() {
|
|
118
|
+
let _cursor;
|
|
119
|
+
let i = 0;
|
|
120
|
+
return {
|
|
121
|
+
next: () => {
|
|
122
|
+
_cursor = _cursor ? _cursor.next : list.head;
|
|
123
|
+
return {
|
|
124
|
+
value: _cursor && i++,
|
|
125
|
+
done: !_cursor,
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Returns the iterator object contains values
|
|
135
|
+
*
|
|
136
|
+
* @return {function}
|
|
137
|
+
*/
|
|
138
|
+
values() {
|
|
139
|
+
const list = this;
|
|
140
|
+
return {
|
|
141
|
+
[Symbol.iterator]() {
|
|
142
|
+
let _cursor;
|
|
143
|
+
return {
|
|
144
|
+
next: () => {
|
|
145
|
+
_cursor = _cursor ? _cursor.next : list.head;
|
|
146
|
+
return {
|
|
147
|
+
value: _cursor && _cursor.value,
|
|
148
|
+
done: !_cursor,
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Tests whether all elements in the list pass the test implemented by
|
|
158
|
+
* the provided function (from left to right)
|
|
159
|
+
*
|
|
160
|
+
* @param {Function} callback - Function to test for each element
|
|
161
|
+
* @param {*} [thisArg] - Value to use as this when executing callback
|
|
162
|
+
* @return {Boolean} - true if the callback function returns a truthy value for every list element; otherwise, false
|
|
163
|
+
* @public
|
|
164
|
+
*/
|
|
165
|
+
every(callback, thisArg) {
|
|
166
|
+
if (typeof callback !== 'function') {
|
|
167
|
+
throw new TypeError('You must provide a function as first argument');
|
|
168
|
+
}
|
|
169
|
+
if (!(this._length && callback)) {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
thisArg = thisArg !== undefined ? thisArg : this;
|
|
173
|
+
let tmp = this._head;
|
|
174
|
+
let nxt;
|
|
175
|
+
let i = 0;
|
|
176
|
+
while (tmp) {
|
|
177
|
+
nxt = tmp.next;
|
|
178
|
+
if (!callback.call(thisArg, tmp.value, i++, thisArg)) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
tmp = nxt;
|
|
182
|
+
}
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Tests whether all elements in the list pass the test implemented by
|
|
188
|
+
* the provided function (from right to left)
|
|
189
|
+
*
|
|
190
|
+
* @param {Function} callback - Function to test for each element
|
|
191
|
+
* @param {*} [thisArg] - Value to use as this when executing callback
|
|
192
|
+
* @return {Boolean} - true if the callback function returns a truthy value for every list element; otherwise, false
|
|
193
|
+
* @public
|
|
194
|
+
*/
|
|
195
|
+
everyRight(callback, thisArg) {
|
|
196
|
+
if (typeof callback !== 'function') {
|
|
197
|
+
throw new TypeError('You must provide a function as first argument');
|
|
198
|
+
}
|
|
199
|
+
if (!(this._length && callback)) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
thisArg = thisArg !== undefined ? thisArg : this;
|
|
203
|
+
let tmp = this.tail;
|
|
204
|
+
for (let i = 0; i < this._length; i++) {
|
|
205
|
+
if (!callback.call(thisArg, tmp.value, this._length - i - 1, thisArg)) {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
tmp = tmp.prev;
|
|
209
|
+
}
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Creates a new list with all elements that pass the test implemented
|
|
215
|
+
* by the provided function
|
|
216
|
+
*
|
|
217
|
+
* @param {Function} callback - Function to test for each element
|
|
218
|
+
* @param {*} [thisArg] - Value to use as this when executing callback
|
|
219
|
+
* @return {DoublyLinked} - A new list with the elements that pass the test
|
|
220
|
+
* @public
|
|
221
|
+
*/
|
|
222
|
+
filter(callback, thisArg) {
|
|
223
|
+
if (typeof callback !== 'function') {
|
|
224
|
+
throw new TypeError('You must provide a function as first argument');
|
|
225
|
+
}
|
|
226
|
+
thisArg = thisArg !== undefined ? thisArg : this;
|
|
227
|
+
let index = 0;
|
|
228
|
+
return this.reduce((acc, value) => {
|
|
229
|
+
if (callback.call(thisArg, value, index++, thisArg)) {
|
|
230
|
+
acc.push(value);
|
|
231
|
+
}
|
|
232
|
+
return acc;
|
|
233
|
+
}, new DoublyLinked());
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Returns the value of the first element in the list that satisfies
|
|
238
|
+
* the provided testing function. Otherwise undefined is returned
|
|
239
|
+
*
|
|
240
|
+
* @param {Function} callback - Function to test for each element
|
|
241
|
+
* @param {*} [thisArg] - Value to use as this when executing callback
|
|
242
|
+
* @return {*} - A value in the list if an element passes the test; otherwise, undefined
|
|
243
|
+
* @public
|
|
244
|
+
*/
|
|
245
|
+
find(callback, thisArg) {
|
|
246
|
+
if (typeof callback !== 'function') {
|
|
247
|
+
throw new TypeError('You must provide a function as first argument');
|
|
248
|
+
}
|
|
249
|
+
if (!this._length) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
thisArg = thisArg !== undefined ? thisArg : this;
|
|
253
|
+
let tmp = this.head;
|
|
254
|
+
for (let i = 0; i < this.length; i++) {
|
|
255
|
+
if (callback.call(thisArg, tmp.value, i, thisArg)) {
|
|
256
|
+
this._cursor = tmp;
|
|
257
|
+
this._eof = false;
|
|
258
|
+
return tmp.value;
|
|
259
|
+
}
|
|
260
|
+
tmp = tmp.next;
|
|
261
|
+
}
|
|
262
|
+
this._cursor = undefined;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Executes a provided function once for each list element (from left to right)
|
|
267
|
+
*
|
|
268
|
+
* @param {Function} callback - Function to execute for each element
|
|
269
|
+
* @param {*} [thisArg] - Value to use as this when executing callback
|
|
270
|
+
* @public
|
|
271
|
+
*/
|
|
272
|
+
forEach(callback, thisArg) {
|
|
273
|
+
this.every((element, index, instance) => {
|
|
274
|
+
callback.call(this, element, index, instance);
|
|
275
|
+
return true;
|
|
276
|
+
}, thisArg);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Executes a provided function once for each list element (from right-to-left)
|
|
281
|
+
*
|
|
282
|
+
* @param {Function} callback - Function to execute for each element
|
|
283
|
+
* @param {*} [thisArg] - Value to use as this when executing callback
|
|
284
|
+
* @public
|
|
285
|
+
*/
|
|
286
|
+
forEachRight(callback, thisArg) {
|
|
287
|
+
this.everyRight((element, index, instance) => {
|
|
288
|
+
callback.call(this, element, index, instance);
|
|
289
|
+
return true;
|
|
290
|
+
}, thisArg);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Determines whether an list includes a certain element,
|
|
295
|
+
* returning true or false as appropriate
|
|
296
|
+
*
|
|
297
|
+
* @param {*} searchElement - The element to search for
|
|
298
|
+
* @param {int} [fromIndex = 0] - The position in this list at which to begin searching for searchElement
|
|
299
|
+
* @return {Boolean} - true if the searchElement found in the list; otherwise, false
|
|
300
|
+
* @public
|
|
301
|
+
*/
|
|
302
|
+
includes(searchElement, fromIndex) {
|
|
303
|
+
const sameValueZero = (x, y) =>
|
|
304
|
+
x === y ||
|
|
305
|
+
(typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
|
|
306
|
+
|
|
307
|
+
fromIndex = fromIndex || 0;
|
|
308
|
+
if (fromIndex < 0) {
|
|
309
|
+
fromIndex = this.length + fromIndex;
|
|
310
|
+
}
|
|
311
|
+
this.find(
|
|
312
|
+
(element, index) =>
|
|
313
|
+
index >= fromIndex && sameValueZero(element, searchElement),
|
|
314
|
+
);
|
|
315
|
+
return !!this.cursor;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Adds one or more elements right after the cursor node of the list and returns
|
|
320
|
+
* the new length of the list
|
|
321
|
+
*
|
|
322
|
+
* @param {*} element... - The elements to add after cursor node
|
|
323
|
+
* @returns {int} - The new length of the list
|
|
324
|
+
* @public
|
|
325
|
+
*/
|
|
326
|
+
insert(...element) {
|
|
327
|
+
for (const arg of element) {
|
|
328
|
+
const node = new Node(this, arg);
|
|
329
|
+
if (this._length) {
|
|
330
|
+
this._cursor.next = node;
|
|
331
|
+
node.prev = this._cursor;
|
|
332
|
+
this._cursor = node;
|
|
333
|
+
} else {
|
|
334
|
+
this._head = node;
|
|
335
|
+
this._tail = node;
|
|
336
|
+
this._cursor = node;
|
|
337
|
+
}
|
|
338
|
+
this._length++;
|
|
339
|
+
this._eof = false;
|
|
340
|
+
}
|
|
341
|
+
return this._length;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Joins all elements of the list into a string and returns this string
|
|
346
|
+
*
|
|
347
|
+
* @param {String} [separator=','] - Specifies a string to separate each pair of adjacent elements of the list
|
|
348
|
+
* @return {String} - A string with all list elements joined. If length is 0, the empty string is returned
|
|
349
|
+
* @public
|
|
350
|
+
*/
|
|
351
|
+
join(separator) {
|
|
352
|
+
separator = separator || ',';
|
|
353
|
+
let out = '';
|
|
354
|
+
this.forEach(value => {
|
|
355
|
+
out += (out ? separator : '') + value;
|
|
356
|
+
});
|
|
357
|
+
return out;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Creates a new list with the results of calling a provided function on
|
|
362
|
+
* every element in the calling list
|
|
363
|
+
*
|
|
364
|
+
* @param {Function} callback - Function that produces an element of the new list
|
|
365
|
+
* @return {DoublyLinked} - A new list with each element being the result of the callback function
|
|
366
|
+
* @public
|
|
367
|
+
*/
|
|
368
|
+
map(callback) {
|
|
369
|
+
if (typeof callback !== 'function') {
|
|
370
|
+
throw new TypeError('You must provide a function as first argument');
|
|
371
|
+
}
|
|
372
|
+
const out = new DoublyLinked();
|
|
373
|
+
this.forEach((value, index, instance) =>
|
|
374
|
+
out.push(callback(value, index, instance)),
|
|
375
|
+
);
|
|
376
|
+
return out.reset();
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Moves cursor to the next and returns its value
|
|
381
|
+
*
|
|
382
|
+
* @return {*} - Returns value of next node to the cursor. If cursor reaches to the end it returns undefined
|
|
383
|
+
* @public
|
|
384
|
+
*/
|
|
385
|
+
next() {
|
|
386
|
+
if (this._cursor === this._tail) {
|
|
387
|
+
this._eof = true;
|
|
388
|
+
return undefined;
|
|
389
|
+
}
|
|
390
|
+
const c = this._cursor ? this._cursor.next : this._head;
|
|
391
|
+
this._cursor = c;
|
|
392
|
+
return c && c.value;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Moves cursor to the previous and returns its value
|
|
397
|
+
*
|
|
398
|
+
* @return {*} - Returns value of previous node to the cursor. If cursor reaches to the head it returns undefined
|
|
399
|
+
* @public
|
|
400
|
+
*/
|
|
401
|
+
prev() {
|
|
402
|
+
let c;
|
|
403
|
+
if (this._eof) {
|
|
404
|
+
this._eof = false;
|
|
405
|
+
c = this._cursor = this._tail;
|
|
406
|
+
return c && c.value;
|
|
407
|
+
}
|
|
408
|
+
c = this._cursor && this._cursor.prev;
|
|
409
|
+
this._cursor = c;
|
|
410
|
+
return c && c.value;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Removes the last element from the list and returns that element
|
|
415
|
+
*
|
|
416
|
+
* @returns {*} - The removed element from the list; undefined if the list is empty.
|
|
417
|
+
* @public
|
|
418
|
+
*/
|
|
419
|
+
pop() {
|
|
420
|
+
const ret = this._tail;
|
|
421
|
+
if (ret) {
|
|
422
|
+
ret.remove();
|
|
423
|
+
return ret.value;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Adds one or more elements to the end of the list and returns
|
|
429
|
+
* the new length of the list
|
|
430
|
+
*
|
|
431
|
+
* @param {*} element... - The elements to add to the end of the list
|
|
432
|
+
* @returns {int} - The new length of the list
|
|
433
|
+
* @public
|
|
434
|
+
*/
|
|
435
|
+
push(...element) {
|
|
436
|
+
if (element.length) {
|
|
437
|
+
this._eof = false;
|
|
438
|
+
}
|
|
439
|
+
for (const arg of element) {
|
|
440
|
+
const node = new Node(this, arg);
|
|
441
|
+
if (this._length) {
|
|
442
|
+
this._tail.next = node;
|
|
443
|
+
node.prev = this._tail;
|
|
444
|
+
this._tail = node;
|
|
445
|
+
} else {
|
|
446
|
+
this._head = node;
|
|
447
|
+
this._tail = node;
|
|
448
|
+
}
|
|
449
|
+
this._length++;
|
|
450
|
+
}
|
|
451
|
+
return this._length;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Applies a function against an accumulator and each element in
|
|
456
|
+
* the list (from left-to-right) to reduce it to a single value
|
|
457
|
+
*
|
|
458
|
+
* @param {Function} callback - Function to execute on each element in the list
|
|
459
|
+
* @param {*} [initialValue] - Value to use as the first argument to the first call of the callback
|
|
460
|
+
* @return {*} - The value that results from the reduction
|
|
461
|
+
* @public
|
|
462
|
+
*/
|
|
463
|
+
reduce(callback, initialValue) {
|
|
464
|
+
if (typeof callback !== 'function') {
|
|
465
|
+
throw new TypeError('You must provide a function as first argument');
|
|
466
|
+
}
|
|
467
|
+
let accumulator;
|
|
468
|
+
let tmp = this._head;
|
|
469
|
+
let i = 0;
|
|
470
|
+
if (arguments.length >= 2) {
|
|
471
|
+
accumulator = initialValue;
|
|
472
|
+
} else {
|
|
473
|
+
if (!this._length) {
|
|
474
|
+
throw new TypeError('Reduce of empty list with no initial value');
|
|
475
|
+
}
|
|
476
|
+
accumulator = tmp.value;
|
|
477
|
+
tmp = tmp.next;
|
|
478
|
+
i = 1;
|
|
479
|
+
}
|
|
480
|
+
while (tmp) {
|
|
481
|
+
accumulator = callback(accumulator, tmp.value, i++, this);
|
|
482
|
+
tmp = tmp.next;
|
|
483
|
+
}
|
|
484
|
+
return accumulator;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Applies a function against an accumulator and each element in
|
|
489
|
+
* the list (from right-to-left) to reduce it to a single value
|
|
490
|
+
*
|
|
491
|
+
* @param {Function} callback - Function to execute on each element in the list
|
|
492
|
+
* @param {*} [initialValue] - Value to use as the first argument to the first call of the callback
|
|
493
|
+
* @return {*} - The value that results from the reduction
|
|
494
|
+
* @public
|
|
495
|
+
*/
|
|
496
|
+
reduceRight(callback, initialValue) {
|
|
497
|
+
if (typeof callback !== 'function') {
|
|
498
|
+
throw new TypeError('You must provide a function as first argument');
|
|
499
|
+
}
|
|
500
|
+
let accumulator;
|
|
501
|
+
let tmp = this._tail;
|
|
502
|
+
let i = this._length - 1;
|
|
503
|
+
if (arguments.length >= 2) {
|
|
504
|
+
accumulator = initialValue;
|
|
505
|
+
} else {
|
|
506
|
+
if (!this._length) {
|
|
507
|
+
throw new TypeError('Reduce of empty list with no initial value');
|
|
508
|
+
}
|
|
509
|
+
accumulator = tmp.value;
|
|
510
|
+
tmp = tmp.prev;
|
|
511
|
+
i--;
|
|
512
|
+
}
|
|
513
|
+
while (tmp) {
|
|
514
|
+
accumulator = callback(accumulator, tmp.value, i--, this);
|
|
515
|
+
tmp = tmp.prev;
|
|
516
|
+
}
|
|
517
|
+
return accumulator;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Removes an element from the list
|
|
522
|
+
*
|
|
523
|
+
* @param {*} element - The element to be removed
|
|
524
|
+
* @param {int} [fromIndex = 0] - The position in this list at which to begin searching for element
|
|
525
|
+
* @return {*} - Returns removed element if found, undefined otherwise
|
|
526
|
+
* @public
|
|
527
|
+
*/
|
|
528
|
+
remove(element, fromIndex) {
|
|
529
|
+
if (this.includes(element, fromIndex)) {
|
|
530
|
+
const cur = this._cursor;
|
|
531
|
+
cur.remove();
|
|
532
|
+
return cur.value;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Resets cursor to head
|
|
538
|
+
*
|
|
539
|
+
* @return {DoublyLinked} - Returns the DoublyLinked instance which this method is called
|
|
540
|
+
* @public
|
|
541
|
+
*/
|
|
542
|
+
reset() {
|
|
543
|
+
this._cursor = undefined;
|
|
544
|
+
this._eof = false;
|
|
545
|
+
return this;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Reverses a list in place. The first array element becomes the last, and the last list element becomes the first.
|
|
550
|
+
*
|
|
551
|
+
* @return {DoublyLinked} - Returns the DoublyLinked instance which this method is called
|
|
552
|
+
* @public
|
|
553
|
+
*/
|
|
554
|
+
reverse() {
|
|
555
|
+
let cur = this._head;
|
|
556
|
+
let p;
|
|
557
|
+
let n;
|
|
558
|
+
for (let i = 0; i < this._length; i++) {
|
|
559
|
+
p = cur.prev;
|
|
560
|
+
n = cur.next;
|
|
561
|
+
cur.prev = n;
|
|
562
|
+
cur.next = p;
|
|
563
|
+
cur = n;
|
|
564
|
+
}
|
|
565
|
+
p = this._head;
|
|
566
|
+
n = this._tail;
|
|
567
|
+
this._head = n;
|
|
568
|
+
this._tail = p;
|
|
569
|
+
this.reset();
|
|
570
|
+
return this;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Removes the first element from the list and returns that element
|
|
575
|
+
*
|
|
576
|
+
* @returns {*} - The removed element from the list; undefined if the list is empty
|
|
577
|
+
* @public
|
|
578
|
+
*/
|
|
579
|
+
shift() {
|
|
580
|
+
const ret = this._head;
|
|
581
|
+
if (ret) {
|
|
582
|
+
ret.remove();
|
|
583
|
+
return ret.value;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Returns a shallow copy of a portion of an array into a new array object
|
|
589
|
+
* selected from start to end (end not included) where start and
|
|
590
|
+
* end represent the index of items in that array.
|
|
591
|
+
*
|
|
592
|
+
* @param {number} [start]
|
|
593
|
+
* @param {number} [end]
|
|
594
|
+
* @returns {Array}
|
|
595
|
+
* @public
|
|
596
|
+
*/
|
|
597
|
+
slice(start, end) {
|
|
598
|
+
start = start || 0;
|
|
599
|
+
const acc = [];
|
|
600
|
+
this.every((value, index) => {
|
|
601
|
+
if (index >= start) {
|
|
602
|
+
acc.push(value);
|
|
603
|
+
}
|
|
604
|
+
return !end || index < end;
|
|
605
|
+
});
|
|
606
|
+
return acc;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Tests whether all elements in the list pass the test implemented by
|
|
611
|
+
* the provided function (from left to right)
|
|
612
|
+
*
|
|
613
|
+
* @param {Function} callback - Function to test for each element
|
|
614
|
+
* @param {*} [thisArg] - Value to use as this when executing callback
|
|
615
|
+
* @public
|
|
616
|
+
*/
|
|
617
|
+
some(callback, thisArg) {
|
|
618
|
+
return !this.every(
|
|
619
|
+
(element, index, instance) =>
|
|
620
|
+
!callback.call(this, element, index, instance),
|
|
621
|
+
thisArg,
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Tests whether at least one element in the list passes the test
|
|
627
|
+
* implemented by the provided function (from right to left)
|
|
628
|
+
*
|
|
629
|
+
* @param {Function} callback - Function to test for each element
|
|
630
|
+
* @param {*} [thisArg] - Value to use as this when executing callback
|
|
631
|
+
* @public
|
|
632
|
+
*/
|
|
633
|
+
someRight(callback, thisArg) {
|
|
634
|
+
return !this.everyRight(
|
|
635
|
+
(element, index, instance) =>
|
|
636
|
+
!callback.call(this, element, index, instance),
|
|
637
|
+
thisArg,
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Returns a new array containing elements of the list
|
|
643
|
+
*
|
|
644
|
+
* @return {Array} - A new Array instance contains elements of the list
|
|
645
|
+
* @public
|
|
646
|
+
*/
|
|
647
|
+
toArray() {
|
|
648
|
+
return this.slice();
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Returns a string representing the specified list and its elements.
|
|
653
|
+
* @return {string} - Returns a string representing the specified list and its elements.
|
|
654
|
+
*/
|
|
655
|
+
toString() {
|
|
656
|
+
return 'DoublyLinked(' + this.join() + ')';
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Adds one or more elements to the beginning of the list
|
|
661
|
+
* the new length of the list
|
|
662
|
+
*
|
|
663
|
+
* @param {*} element... - The elements to add to the front of the list
|
|
664
|
+
* @returns {int} - The new length of the list
|
|
665
|
+
* @public
|
|
666
|
+
*/
|
|
667
|
+
unshift(...element) {
|
|
668
|
+
for (const arg of element) {
|
|
669
|
+
const node = new Node(this, arg);
|
|
670
|
+
if (this._length) {
|
|
671
|
+
this._head.prev = node;
|
|
672
|
+
node.next = this._head;
|
|
673
|
+
this._head = node;
|
|
674
|
+
} else {
|
|
675
|
+
this._head = node;
|
|
676
|
+
this._tail = node;
|
|
677
|
+
}
|
|
678
|
+
this._length++;
|
|
679
|
+
}
|
|
680
|
+
return this._length;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Returns the iterator object contains entries
|
|
685
|
+
*
|
|
686
|
+
* @return {Object} - Returns the iterator object contains entries
|
|
687
|
+
*/
|
|
688
|
+
[Symbol.iterator]() {
|
|
689
|
+
let _cursor;
|
|
690
|
+
return {
|
|
691
|
+
next: () => {
|
|
692
|
+
_cursor = _cursor ? _cursor.next : this.head;
|
|
693
|
+
return {
|
|
694
|
+
value: _cursor && _cursor.value,
|
|
695
|
+
done: !_cursor,
|
|
696
|
+
};
|
|
697
|
+
},
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
*
|
|
704
|
+
* @constructor
|
|
705
|
+
*/
|
|
706
|
+
class Node {
|
|
707
|
+
constructor(list, value) {
|
|
708
|
+
this.list = list;
|
|
709
|
+
this.value = value;
|
|
710
|
+
this.prev = undefined;
|
|
711
|
+
this.next = undefined;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
remove() {
|
|
715
|
+
if (!this.list) {
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
if (this.prev) {
|
|
719
|
+
// noinspection JSUnresolvedVariable
|
|
720
|
+
this.prev.next = this.next;
|
|
721
|
+
}
|
|
722
|
+
if (this.next) {
|
|
723
|
+
// noinspection JSUnresolvedVariable
|
|
724
|
+
this.next.prev = this.prev;
|
|
725
|
+
}
|
|
726
|
+
if (this === this.list._cursor) {
|
|
727
|
+
this.list._cursor = this.next || this.prev;
|
|
728
|
+
}
|
|
729
|
+
if (this === this.list._head) {
|
|
730
|
+
this.list._head = this.next;
|
|
731
|
+
}
|
|
732
|
+
if (this === this.list._tail) {
|
|
733
|
+
this.list._tail = this.prev;
|
|
734
|
+
}
|
|
735
|
+
this.list._length--;
|
|
736
|
+
this.prev = undefined;
|
|
737
|
+
this.next = undefined;
|
|
738
|
+
this.list = undefined;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Expose `DoublyLinked`.
|
|
744
|
+
*/
|
|
745
|
+
|
|
746
|
+
module.exports = DoublyLinked;
|