list-toolkit 1.0.2 → 2.1.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.
Files changed (88) hide show
  1. package/README.md +89 -419
  2. package/cjs/cache/cache-fifo.js +37 -0
  3. package/cjs/cache/cache-lfu.js +76 -0
  4. package/cjs/cache/cache-lru.js +100 -0
  5. package/cjs/cache/cache-random.js +77 -0
  6. package/cjs/cache/decorator.js +47 -0
  7. package/cjs/cache.js +28 -0
  8. package/cjs/ext-list.js +22 -0
  9. package/cjs/ext-slist.js +22 -0
  10. package/cjs/ext-value-list.js +22 -0
  11. package/cjs/ext-value-slist.js +22 -0
  12. package/cjs/{MinHeap.js → heap/min-heap.js} +68 -82
  13. package/cjs/heap.js +22 -0
  14. package/cjs/list/basics.js +88 -0
  15. package/cjs/list/core.js +305 -0
  16. package/cjs/list/ext-value.js +89 -0
  17. package/cjs/list/ext.js +356 -0
  18. package/cjs/list/nodes.js +240 -0
  19. package/cjs/list/ptr.js +61 -0
  20. package/cjs/list/value.js +100 -0
  21. package/cjs/list-helpers.js +91 -0
  22. package/cjs/list-utils.js +141 -0
  23. package/cjs/list.js +22 -0
  24. package/cjs/meta-utils.js +167 -0
  25. package/cjs/nt-utils.js +132 -0
  26. package/cjs/queue.js +58 -0
  27. package/cjs/slist/basics.js +71 -0
  28. package/cjs/slist/core.js +362 -0
  29. package/cjs/slist/ext-value.js +83 -0
  30. package/cjs/slist/ext.js +336 -0
  31. package/cjs/slist/nodes.js +276 -0
  32. package/cjs/slist/ptr.js +87 -0
  33. package/cjs/slist/value.js +91 -0
  34. package/cjs/slist.js +22 -0
  35. package/cjs/stack.js +55 -0
  36. package/cjs/tree/splay-tree.js +362 -0
  37. package/cjs/value-list.js +22 -0
  38. package/cjs/value-slist.js +22 -0
  39. package/package.json +7 -7
  40. package/src/cache/cache-fifo.js +27 -0
  41. package/src/cache/cache-lfu.js +63 -0
  42. package/src/cache/cache-lru.js +87 -0
  43. package/src/cache/cache-random.js +73 -0
  44. package/src/cache/decorator.js +45 -0
  45. package/src/cache.js +9 -0
  46. package/src/ext-list.js +6 -0
  47. package/src/ext-slist.js +6 -0
  48. package/src/ext-value-list.js +6 -0
  49. package/src/ext-value-slist.js +6 -0
  50. package/src/{MinHeap.js → heap/min-heap.js} +73 -85
  51. package/src/heap.js +6 -0
  52. package/src/list/basics.js +64 -0
  53. package/src/list/core.js +314 -0
  54. package/src/list/ext-value.js +81 -0
  55. package/src/list/ext.js +370 -0
  56. package/src/list/nodes.js +262 -0
  57. package/src/list/ptr.js +58 -0
  58. package/src/list/value.js +88 -0
  59. package/src/list-helpers.js +80 -0
  60. package/src/list-utils.js +140 -0
  61. package/src/list.js +6 -0
  62. package/src/meta-utils.js +147 -0
  63. package/src/nt-utils.js +85 -0
  64. package/src/queue.js +52 -0
  65. package/src/slist/basics.js +47 -0
  66. package/src/slist/core.js +364 -0
  67. package/src/slist/ext-value.js +74 -0
  68. package/src/slist/ext.js +331 -0
  69. package/src/slist/nodes.js +290 -0
  70. package/src/slist/ptr.js +77 -0
  71. package/src/slist/value.js +75 -0
  72. package/src/slist.js +6 -0
  73. package/src/stack.js +52 -0
  74. package/src/tree/splay-tree.js +378 -0
  75. package/src/value-list.js +6 -0
  76. package/src/value-slist.js +6 -0
  77. package/cjs/Cache.js +0 -71
  78. package/cjs/List.js +0 -294
  79. package/cjs/ListHead.js +0 -309
  80. package/cjs/SList.js +0 -342
  81. package/cjs/SListHead.js +0 -367
  82. package/cjs/utils.js +0 -43
  83. package/src/Cache.js +0 -61
  84. package/src/List.js +0 -303
  85. package/src/ListHead.js +0 -304
  86. package/src/SList.js +0 -330
  87. package/src/SListHead.js +0 -354
  88. package/src/utils.js +0 -35
@@ -0,0 +1,87 @@
1
+ 'use strict';
2
+
3
+ import ValueList from '../value-list.js';
4
+ import {addAliases} from '../meta-utils.js';
5
+
6
+ // The base cache class. Evicts the least recently used items.
7
+ // Based on doubly linked value lists.
8
+
9
+ export class CacheLRU {
10
+ constructor(capacity = 10) {
11
+ this.capacity = capacity;
12
+ this.list = new ValueList();
13
+ this.dict = new Map();
14
+ }
15
+ get isEmpty() {
16
+ return !this.dict.size;
17
+ }
18
+ get size() {
19
+ return this.dict.size;
20
+ }
21
+ has(key) {
22
+ return this.dict.has(key);
23
+ }
24
+ find(key) {
25
+ const node = this.use(key);
26
+ return node ? node.value.value : undefined;
27
+ }
28
+ remove(key) {
29
+ const node = this.dict.get(key);
30
+ if (node) {
31
+ this.dict.delete(key);
32
+ this.list.removeNode(node);
33
+ }
34
+ return this;
35
+ }
36
+ register(key, value) {
37
+ const node = this.use(key);
38
+ if (node) {
39
+ this.update(node, value);
40
+ return this;
41
+ }
42
+ if (this.dict.size >= this.capacity) {
43
+ this.evictAndReplace(key, value);
44
+ return this;
45
+ }
46
+ this.addNew(key, value);
47
+ return this;
48
+ }
49
+ use(key) {
50
+ const node = this.dict.get(key);
51
+ if (node) this.list.moveToFront(node);
52
+ return node;
53
+ }
54
+ update(node, value) {
55
+ node.value.value = value;
56
+ return this;
57
+ }
58
+ addNew(key, value) {
59
+ this.list.pushFront({key, value});
60
+ const node = this.list.front;
61
+ this.dict.set(key, node);
62
+ return node;
63
+ }
64
+ evictAndReplace(key, value) {
65
+ const node = this.list.back;
66
+ this.list.moveToFront(node);
67
+ this.dict.delete(node.value.key);
68
+ this.dict.set(key, node);
69
+ node.value = {key, value};
70
+ return node;
71
+ }
72
+ clear() {
73
+ this.dict.clear();
74
+ this.list.clear();
75
+ return this;
76
+ }
77
+ [Symbol.iterator]() {
78
+ return this.list[Symbol.iterator]();
79
+ }
80
+ getReverseIterator() {
81
+ return this.list.getReverseIterator();
82
+ }
83
+ }
84
+
85
+ addAliases(CacheLRU.prototype, {register: 'add, set', remove: 'delete', find: 'get'});
86
+
87
+ export default CacheLRU;
@@ -0,0 +1,73 @@
1
+ 'use strict';
2
+
3
+ import {addAlias} from '../meta-utils.js';
4
+ import MinHeap from '../heap/min-heap.js';
5
+ import CacheLRU from './cache-lru.js';
6
+
7
+ // Evicts items randomly.
8
+
9
+ export class CacheRandom extends CacheLRU {
10
+ constructor(capacity = 10) {
11
+ super(capacity);
12
+ this.heap = new MinHeap({less: (a, b) => a.value.id > b.value.id});
13
+ this.nextId = 0;
14
+ }
15
+ use(key) {
16
+ return this.dict.get(key);
17
+ }
18
+ update(node, value) {
19
+ node.value.value = value;
20
+ return this;
21
+ }
22
+ addNew(key, value) {
23
+ this.list.pushFront({key, value, id: this.nextId++});
24
+ const node = this.list.front;
25
+ this.dict.set(key, node);
26
+ this.heap.push(node);
27
+ return node;
28
+ }
29
+ evictAndReplace(key, value) {
30
+ const index = Math.floor(this.heap.length * Math.random());
31
+
32
+ const node = this.heap.array[index],
33
+ isDecreased = value > node.value.value;
34
+
35
+ this.dict.delete(node.value.key);
36
+ this.dict.set(key, node);
37
+
38
+ node.value.key = key;
39
+ node.value.value = value;
40
+
41
+ this.heap.updateByIndex(index, isDecreased);
42
+
43
+ return node;
44
+ }
45
+ remove(key) {
46
+ const node = this.dict.get(key);
47
+ if (node) {
48
+ this.dict.delete(key);
49
+ this.list.removeNode(node);
50
+ this.heap.remove(node);
51
+ }
52
+ return this;
53
+ }
54
+ clear() {
55
+ super.clear();
56
+ this.heap.clear();
57
+ this.nextId = 0;
58
+ return this;
59
+ }
60
+ resetIds() {
61
+ this.nextId = 0;
62
+ for (const item of this.heap) {
63
+ item.id = this.nextId++;
64
+ }
65
+ const array = this.heap.array;
66
+ this.heap.clear().merge(array);
67
+ return this;
68
+ }
69
+ }
70
+
71
+ addAlias(CacheRandom.prototype, 'remove', 'delete');
72
+
73
+ export default CacheRandom;
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ export const decorateFn = (fn, cache) => {
4
+ if (typeof fn !== 'function') throw new TypeError('Not a function');
5
+ const wrapped = function (...args) {
6
+ const key = args[0],
7
+ cache = wrapped.cache;
8
+ if (cache.has(key)) return cache.get(key);
9
+ const result = wrapped.fn.apply(this, args);
10
+ cache.set(key, result);
11
+ return result;
12
+ };
13
+ wrapped.fn = fn;
14
+ wrapped.cache = cache;
15
+ return wrapped;
16
+ };
17
+
18
+ export const decorate = (object, key, cache) => {
19
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
20
+ if (!descriptor) throw new Error('Missing property: ' + key);
21
+
22
+ const newDescriptor = {...descriptor},
23
+ wrapped = decorateFn(descriptor.value, cache);
24
+
25
+ newDescriptor.value = wrapped;
26
+
27
+ Object.defineProperty(object, key, newDescriptor);
28
+
29
+ return wrapped;
30
+ };
31
+
32
+ export const decorateMethod = (object, key, cache) => {
33
+ const fn = object[key],
34
+ wrapped = decorateFn(fn, cache);
35
+ object[key] = wrapped;
36
+ return wrapped;
37
+ };
38
+
39
+ export const getCache = (object, key) => {
40
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
41
+ if (!descriptor) throw new Error('Missing property: ' + key);
42
+ return descriptor.value.cache;
43
+ };
44
+
45
+ export default decorate;
package/src/cache.js ADDED
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ export * from './cache/cache-lru.js';
4
+ import Cache from './cache/cache-lru.js';
5
+ import decorator from './cache/decorator.js';
6
+
7
+ export const cacheDecorator = (object, key, cache = new Cache()) => decorator(object, key, cache);
8
+
9
+ export default Cache;
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+
3
+ export * from './list/ext.js';
4
+ import ExtList from './list/ext.js';
5
+
6
+ export default ExtList;
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+
3
+ export * from './slist/ext.js';
4
+ import ExtSList from './slist/ext.js';
5
+
6
+ export default ExtSList;
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+
3
+ export * from './list/ext-value.js';
4
+ import ExtValueList from './list/ext-value.js';
5
+
6
+ export default ExtValueList;
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+
3
+ export * from './slist/ext-value.js';
4
+ import ExtValueSList from './slist/ext-value.js';
5
+
6
+ export default ExtValueSList;
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- import {copyOptions} from './utils.js';
3
+ import {copyOptions} from '../meta-utils.js';
4
4
 
5
5
  // the following functions are inlined:
6
6
 
@@ -26,17 +26,10 @@ const down = (array, i, less = defaultLess, n = array.length) => {
26
26
  for (;;) {
27
27
  const l = (i << 1) + 1;
28
28
  if (l >= n) break;
29
- let c = l,
29
+ const r = l + 1,
30
+ c = r < n && less(array[r], array[l]) ? r : l,
31
+ iValue = array[i],
30
32
  cValue = array[c];
31
- const r = c + 1;
32
- if (r < n) {
33
- const rValue = array[r];
34
- if (less(rValue, cValue)) {
35
- c = r;
36
- cValue = rValue;
37
- }
38
- }
39
- const iValue = array[i];
40
33
  if (!less(cValue, iValue)) break;
41
34
  array[i] = cValue;
42
35
  array[c] = iValue;
@@ -48,6 +41,10 @@ const down = (array, i, less = defaultLess, n = array.length) => {
48
41
  export class MinHeap {
49
42
  constructor(options, ...args) {
50
43
  copyOptions(this, MinHeap.defaults, options);
44
+ if (typeof this.compare == 'function') {
45
+ this.less = (a, b) => this.compare(a, b) < 0;
46
+ this.equal = (a, b) => !this.compare(a, b);
47
+ }
51
48
  this.array = [];
52
49
  this.merge(...args);
53
50
  }
@@ -64,9 +61,8 @@ export class MinHeap {
64
61
  return this.array[0];
65
62
  }
66
63
 
67
- clear() {
68
- this.array = [];
69
- return this;
64
+ peek() {
65
+ return this.array[0];
70
66
  }
71
67
 
72
68
  pop() {
@@ -84,17 +80,10 @@ export class MinHeap {
84
80
  for (let i = 0; ; ) {
85
81
  const l = (i << 1) + 1;
86
82
  if (l >= n) break;
87
- let c = l,
83
+ const r = l + 1,
84
+ c = r < n && this.less(this.array[r], this.array[l]) ? r : l,
85
+ iValue = this.array[i],
88
86
  cValue = this.array[c];
89
- const r = c + 1;
90
- if (r < n) {
91
- const rValue = this.array[r];
92
- if (this.less(rValue, cValue)) {
93
- c = r;
94
- cValue = rValue;
95
- }
96
- }
97
- const iValue = this.array[i];
98
87
  if (!this.less(cValue, iValue)) break;
99
88
  this.array[i] = cValue;
100
89
  this.array[c] = iValue;
@@ -128,17 +117,10 @@ export class MinHeap {
128
117
  for (let i = 0; ; ) {
129
118
  const l = (i << 1) + 1;
130
119
  if (l >= n) break;
131
- let c = l,
120
+ const r = l + 1,
121
+ c = r < n && this.less(this.array[r], this.array[l]) ? r : l,
122
+ iValue = this.array[i],
132
123
  cValue = this.array[c];
133
- const r = c + 1;
134
- if (r < n) {
135
- const rValue = this.array[r];
136
- if (this.less(rValue, cValue)) {
137
- c = r;
138
- cValue = rValue;
139
- }
140
- }
141
- const iValue = this.array[i];
142
124
  if (!this.less(cValue, iValue)) break;
143
125
  this.array[i] = cValue;
144
126
  this.array[c] = iValue;
@@ -156,17 +138,10 @@ export class MinHeap {
156
138
  for (let i = 0; ; ) {
157
139
  const l = (i << 1) + 1;
158
140
  if (l >= n) break;
159
- let c = l,
141
+ const r = l + 1,
142
+ c = r < n && this.less(this.array[r], this.array[l]) ? r : l,
143
+ iValue = this.array[i],
160
144
  cValue = this.array[c];
161
- const r = c + 1;
162
- if (r < n) {
163
- const rValue = this.array[r];
164
- if (this.less(rValue, cValue)) {
165
- c = r;
166
- cValue = rValue;
167
- }
168
- }
169
- const iValue = this.array[i];
170
145
  if (!this.less(cValue, iValue)) break;
171
146
  this.array[i] = cValue;
172
147
  this.array[c] = iValue;
@@ -180,12 +155,43 @@ export class MinHeap {
180
155
  return this.array.findIndex(element => this.equal(element, value)) >= 0;
181
156
  }
182
157
 
158
+ findIndex(value) {
159
+ return this.array.findIndex(element => this.equal(element, value));
160
+ }
161
+
183
162
  remove(value) {
184
- return MinHeap.remove(this.array, value, this.less, this.equal);
163
+ MinHeap.remove(this.array, value, this.less, this.equal);
164
+ return this;
165
+ }
166
+
167
+ removeByIndex(index) {
168
+ MinHeap.removeByIndex(this.array, index, this.less);
169
+ return this;
185
170
  }
186
171
 
187
172
  replace(value, newValue) {
188
- return MinHeap.replace(this.array, value, newValue, this.less, this.equal);
173
+ MinHeap.replace(this.array, value, newValue, this.less, this.equal);
174
+ return this;
175
+ }
176
+
177
+ replaceByIndex(index, newValue) {
178
+ MinHeap.replaceByIndex(this.array, index, newValue, this.less);
179
+ return this;
180
+ }
181
+
182
+ updateTop() {
183
+ down(this.array, 0, this.less);
184
+ return this;
185
+ }
186
+
187
+ updateByIndex(index, isDecreased) {
188
+ MinHeap.updateByIndex(this.array, index, isDecreased, this.less);
189
+ return this;
190
+ }
191
+
192
+ clear() {
193
+ this.array = [];
194
+ return this;
189
195
  }
190
196
 
191
197
  releaseSorted() {
@@ -227,17 +233,10 @@ export class MinHeap {
227
233
  for (let i = j; ; ) {
228
234
  const l = (i << 1) + 1;
229
235
  if (l >= n) break;
230
- let c = l,
236
+ const r = l + 1,
237
+ c = r < n && less(array[r], array[l]) ? r : l,
238
+ iValue = array[i],
231
239
  cValue = array[c];
232
- const r = c + 1;
233
- if (r < n) {
234
- const rValue = array[r];
235
- if (less(rValue, cValue)) {
236
- c = r;
237
- cValue = rValue;
238
- }
239
- }
240
- const iValue = array[i];
241
240
  if (!less(cValue, iValue)) break;
242
241
  array[i] = cValue;
243
242
  array[c] = iValue;
@@ -268,10 +267,7 @@ export class MinHeap {
268
267
 
269
268
  static pushPop(heapArray, item, less = MinHeap.defaults.less) {
270
269
  if (!heapArray.length || less(item, heapArray[0])) return item;
271
- const top = heapArray[0];
272
- heapArray[0] = item;
273
- down(heapArray, 0, less);
274
- return top;
270
+ return MinHeap.replaceTop(heapArray, item, less);
275
271
  }
276
272
 
277
273
  static replaceTop(heapArray, item, less = MinHeap.defaults.less) {
@@ -289,46 +285,38 @@ export class MinHeap {
289
285
  return heapArray.findIndex(element => equal(element, item));
290
286
  }
291
287
 
292
- static removeByIndex(heapArray, index, less = MinHeap.defaults.less, equal = MinHeap.defaults.equal) {
288
+ static removeByIndex(heapArray, index, less = MinHeap.defaults.less) {
293
289
  if (index < 0 || index >= heapArray.length) return this;
294
290
  const last = heapArray.length - 1;
295
291
  if (index !== last) {
296
- const item = heapArray[index];
297
- heapArray[index] = heapArray.pop();
298
- if (less(heapArray[index], item)) up(heapArray, index, less);
299
- else down(heapArray, index, less);
300
- } else heapArray.pop();
301
- return this;
292
+ const item = heapArray[index],
293
+ newItem = (heapArray[index] = heapArray.pop());
294
+ return MinHeap.updateByIndex(heapArray, index, less(newItem, item), less);
295
+ }
296
+ heapArray.pop();
297
+ return heapArray;
302
298
  }
303
299
 
304
300
  static remove(heapArray, item, less = MinHeap.defaults.less, equal = MinHeap.defaults.equal) {
305
301
  const index = heapArray.findIndex(element => equal(element, item));
306
- if (index < 0) return this;
307
- const last = heapArray.length - 1;
308
- if (index !== last) {
309
- heapArray[index] = heapArray.pop();
310
- if (less(heapArray[index], item)) up(heapArray, index, less);
311
- else down(heapArray, index, less);
312
- } else heapArray.pop();
313
- return this;
302
+ return MinHeap.removeByIndex(heapArray, index, less);
314
303
  }
315
304
 
316
- static replaceByIndex(heapArray, index, newItem, less = MinHeap.defaults.less, equal = MinHeap.defaults.equal) {
305
+ static replaceByIndex(heapArray, index, newItem, less = MinHeap.defaults.less) {
317
306
  if (index < 0 || index >= heapArray.length) return this;
318
307
  const item = heapArray[index];
319
308
  heapArray[index] = newItem;
320
- if (less(newItem, item)) up(heapArray, index, less);
321
- else down(heapArray, index, less);
322
- return this;
309
+ return MinHeap.updateByIndex(heapArray, index, less(newItem, item), less);
323
310
  }
324
311
 
325
312
  static replace(heapArray, item, newItem, less = MinHeap.defaults.less, equal = MinHeap.defaults.equal) {
326
313
  const index = heapArray.findIndex(element => equal(element, item));
327
- if (index < 0) return this;
328
- heapArray[index] = newItem;
329
- if (less(newItem, item)) up(heapArray, index, less);
330
- else down(heapArray, index, less);
331
- return this;
314
+ return MinHeap.replaceByIndex(heapArray, index, newItem, less);
315
+ }
316
+
317
+ static updateByIndex(heapArray, index, isDecreased, less = MinHeap.defaults.less) {
318
+ if (index < 0 || index >= heapArray.length) return this;
319
+ return (isDecreased ? up : down)(heapArray, index, less);
332
320
  }
333
321
 
334
322
  static sort(heapArray, less = MinHeap.defaults.less) {
@@ -341,6 +329,6 @@ export class MinHeap {
341
329
  }
342
330
  }
343
331
 
344
- MinHeap.defaults = {less: defaultLess, equal: defaultEqual};
332
+ MinHeap.defaults = {less: defaultLess, equal: defaultEqual, compare: null};
345
333
 
346
334
  export default MinHeap;
package/src/heap.js ADDED
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+
3
+ export * from './heap/min-heap.js';
4
+ import Heap from './heap/min-heap.js';
5
+
6
+ export default Heap;
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+
3
+ // useful low-level operations on doubly linked lists
4
+
5
+ export const extract = ({nextName, prevName}, {from, to = from}) => {
6
+ const next = to[nextName],
7
+ prev = from[prevName];
8
+
9
+ // extract
10
+ prev[nextName] = next;
11
+ next[prevName] = prev;
12
+
13
+ // clear
14
+ from[prevName] = to;
15
+ to[nextName] = from;
16
+
17
+ return {extracted: from, rest: next === from ? null : next};
18
+ };
19
+
20
+ // pop(options, head).node === extract(options, {from: node})
21
+
22
+ export const pop = ({nextName, prevName}, node) => {
23
+ const next = node[nextName],
24
+ prev = node[prevName];
25
+
26
+ // extract
27
+ prev[nextName] = next;
28
+ next[prevName] = prev;
29
+
30
+ // clear
31
+ node[prevName] = node[nextName] = node;
32
+
33
+ return {extracted: node, rest: next === node ? null : next};
34
+ };
35
+
36
+ export const splice = ({nextName, prevName}, target, circularList) => {
37
+ const next = target[nextName],
38
+ from = circularList,
39
+ to = from[prevName];
40
+
41
+ // splice
42
+ target[nextName] = from;
43
+ from[prevName] = target;
44
+ to[nextName] = next;
45
+ next[prevName] = to;
46
+
47
+ return target;
48
+ };
49
+
50
+ // append(options, target, range) === splice(options, target, extract(options, range))
51
+
52
+ export const append = ({nextName, prevName}, target, {from, to = from}) => {
53
+ // extract
54
+ from[prevName][nextName] = to[nextName];
55
+ to[nextName][prevName] = from[prevName];
56
+
57
+ // splice
58
+ to[nextName] = target[nextName];
59
+ to[nextName][prevName] = to;
60
+ target[nextName] = from;
61
+ from[prevName] = target;
62
+
63
+ return target;
64
+ };