list-toolkit 1.0.1 → 2.0.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 +93 -407
- package/cjs/cache/cache-fifo.js +37 -0
- package/cjs/cache/cache-lfu.js +76 -0
- package/cjs/cache/cache-lru.js +100 -0
- package/cjs/cache/cache-random.js +77 -0
- package/cjs/cache/decorator.js +47 -0
- package/cjs/cache.js +28 -0
- package/cjs/ext-list.js +22 -0
- package/cjs/ext-slist.js +22 -0
- package/cjs/ext-value-list.js +22 -0
- package/cjs/ext-value-slist.js +22 -0
- package/cjs/{MinHeap.js → heap/min-heap.js} +77 -10
- package/cjs/heap.js +22 -0
- package/cjs/list/basics.js +88 -0
- package/cjs/list/core.js +305 -0
- package/cjs/list/ext-value.js +89 -0
- package/cjs/list/ext.js +356 -0
- package/cjs/list/nodes.js +240 -0
- package/cjs/list/ptr.js +61 -0
- package/cjs/list/value.js +100 -0
- package/cjs/list-helpers.js +91 -0
- package/cjs/list-utils.js +141 -0
- package/cjs/list.js +22 -0
- package/cjs/meta-utils.js +167 -0
- package/cjs/nt-utils.js +132 -0
- package/cjs/queue.js +58 -0
- package/cjs/slist/basics.js +71 -0
- package/cjs/slist/core.js +362 -0
- package/cjs/slist/ext-value.js +83 -0
- package/cjs/slist/ext.js +336 -0
- package/cjs/slist/nodes.js +276 -0
- package/cjs/slist/ptr.js +87 -0
- package/cjs/slist/value.js +91 -0
- package/cjs/slist.js +22 -0
- package/cjs/stack.js +55 -0
- package/cjs/value-list.js +22 -0
- package/cjs/value-slist.js +22 -0
- package/package.json +10 -8
- package/src/cache/cache-fifo.js +27 -0
- package/src/cache/cache-lfu.js +63 -0
- package/src/cache/cache-lru.js +87 -0
- package/src/cache/cache-random.js +73 -0
- package/src/cache/decorator.js +45 -0
- package/src/cache.js +9 -0
- package/src/ext-list.js +6 -0
- package/src/ext-slist.js +6 -0
- package/src/ext-value-list.js +6 -0
- package/src/ext-value-slist.js +6 -0
- package/src/{MinHeap.js → heap/min-heap.js} +91 -9
- package/src/heap.js +6 -0
- package/src/list/basics.js +64 -0
- package/src/list/core.js +314 -0
- package/src/list/ext-value.js +81 -0
- package/src/list/ext.js +370 -0
- package/src/list/nodes.js +262 -0
- package/src/list/ptr.js +58 -0
- package/src/list/value.js +88 -0
- package/src/list-helpers.js +80 -0
- package/src/list-utils.js +140 -0
- package/src/list.js +6 -0
- package/src/meta-utils.js +147 -0
- package/src/nt-utils.js +85 -0
- package/src/queue.js +52 -0
- package/src/slist/basics.js +47 -0
- package/src/slist/core.js +364 -0
- package/src/slist/ext-value.js +74 -0
- package/src/slist/ext.js +331 -0
- package/src/slist/nodes.js +290 -0
- package/src/slist/ptr.js +77 -0
- package/src/slist/value.js +75 -0
- package/src/slist.js +6 -0
- package/src/stack.js +52 -0
- package/src/value-list.js +6 -0
- package/src/value-slist.js +6 -0
- package/cjs/Cache.js +0 -70
- package/cjs/List.js +0 -291
- package/cjs/ListHead.js +0 -308
- package/cjs/SList.js +0 -336
- package/cjs/SListHead.js +0 -363
- package/cjs/utils.js +0 -43
- package/src/Cache.js +0 -61
- package/src/List.js +0 -303
- package/src/ListHead.js +0 -304
- package/src/SList.js +0 -329
- package/src/SListHead.js +0 -353
- package/src/utils.js +0 -35
package/README.md
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
# List toolkit
|
|
1
|
+
# List toolkit [![NPM version][npm-img]][npm-url]
|
|
2
|
+
|
|
3
|
+
[npm-img]: https://img.shields.io/npm/v/list-toolkit.svg
|
|
4
|
+
[npm-url]: https://npmjs.org/package/list-toolkit
|
|
2
5
|
|
|
3
6
|
List-based **efficient** data structures to organize your objects.
|
|
4
7
|
This is a pure JavaScript module with no dependencies
|
|
@@ -6,455 +9,136 @@ suitable to use in all environments including browsers.
|
|
|
6
9
|
|
|
7
10
|
The toolkit provides the following data structures with a full set of efficiently implemented operations:
|
|
8
11
|
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Usage
|
|
24
|
-
|
|
25
|
-
The full documentation is available in the project's [wiki](https://github.com/uhop/list-toolkit/wiki). Below is a cheat sheet of the API.
|
|
26
|
-
|
|
27
|
-
### List
|
|
28
|
-
|
|
29
|
-
`List` implements a circular doubly linked list. Its head is a node with two properties: `next` and `prev`.
|
|
30
|
-
Other nodes of the list are value nodes (`List.ValueNode`) that have a `value` property.
|
|
31
|
-
|
|
32
|
-
```js
|
|
33
|
-
import List from 'list-toolkit/List.js';
|
|
34
|
-
// or
|
|
35
|
-
// const List = require('list-toolkit/List.js').default; // CJS
|
|
36
|
-
|
|
37
|
-
const list1 = new List();
|
|
38
|
-
const list2 = List.from([1, 2, 3]);
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Main operations are:
|
|
42
|
-
|
|
43
|
-
| Method | Description | Complexity |
|
|
44
|
-
|------|-----------|-----|
|
|
45
|
-
| `new List()` | create a new List | *O(1)* |
|
|
46
|
-
| `List.from(values)` | create a new List from an iterable or an array | *O(k)* |
|
|
47
|
-
| `isEmpty` | check if the list is empty | *O(1)* |
|
|
48
|
-
| `front` | get the first element | *O(1)* |
|
|
49
|
-
| `back` | get the last element | *O(1)* |
|
|
50
|
-
| `getLength()` | get the length of the list | *O(n)* |
|
|
51
|
-
| `popFront()` | remove and return the first element | *O(1)* |
|
|
52
|
-
| `popBack()` | remove and return the last element | *O(1)* |
|
|
53
|
-
| `pushFront(value)` | add a new element at the beginning | *O(1)* |
|
|
54
|
-
| `pushBack(value)` | add a new element at the end | *O(1)* |
|
|
55
|
-
| `appendFront(list)` | add a list at the beginning | *O(1)* |
|
|
56
|
-
| `appendBack(list)` | add a list at the end | *O(1)* |
|
|
57
|
-
| `moveToFront(node)` | move a node to the front | *O(1)* |
|
|
58
|
-
| `moveToBack(node)` | move a node to the back | *O(1)* |
|
|
59
|
-
| `clear()` | remove all elements | *O(1)* |
|
|
60
|
-
| `remove(from, to = from)` | remove a range of elements | *O(1)* |
|
|
61
|
-
| `extract(from, to)` | remove and return a range of elements as a new List | *O(1)* |
|
|
62
|
-
| `reverse()` | reverse the list inline | *O(n)* |
|
|
63
|
-
| `sort(compareFn)` | sort the list inline | *O(n * log(n))* |
|
|
64
|
-
|
|
65
|
-
Here and everywhere `n` is a number of elements in the list, while `k` is the size of an argument.
|
|
66
|
-
|
|
67
|
-
Useful aliases are:
|
|
68
|
-
|
|
69
|
-
| Method | Alias of |
|
|
70
|
-
|------|-----------|
|
|
71
|
-
| `pop()` | `popFront()` |
|
|
72
|
-
| `push()` | `pushFront()` |
|
|
73
|
-
| `append()` | `appendBack()` |
|
|
74
|
-
|
|
75
|
-
List can be used as an iterable:
|
|
76
|
-
|
|
77
|
-
```js
|
|
78
|
-
const list = List.from([1, 2, 3]);
|
|
79
|
-
|
|
80
|
-
for (const value of list) {
|
|
81
|
-
console.log(value);
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Additional iterator-related methods are:
|
|
86
|
-
|
|
87
|
-
| Method | Description | Complexity |
|
|
88
|
-
|------|-----------|-----|
|
|
89
|
-
| `getIterable(from, to)` | get an iterable of a range | *O(1)* |
|
|
90
|
-
| `getReverseIterable(from, to)` | get an iterable of a range in reverse order | *O(1)* |
|
|
91
|
-
| `getNodeIterable(from, to)` | get an iterable of a range by nodes | *O(1)* |
|
|
92
|
-
| `getNodeReverseIterable(from, to)` | get an iterable of a range by nodes in reverse order | *O(1)* |
|
|
93
|
-
|
|
94
|
-
Helper methods are:
|
|
95
|
-
|
|
96
|
-
| Method | Description | Complexity |
|
|
97
|
-
|------|-----------|-----|
|
|
98
|
-
| `makeFrom(values)` | (a meta helper) create a new List from an iterable or an array | *O(k)* |
|
|
99
|
-
| `pushValuesFront(values)` | add values at the beginning | *O(k)* |
|
|
100
|
-
| `pushValuesBack(values)` | add values at the end | *O(k)* |
|
|
101
|
-
| `appendValuesFront(values)` | add values as a list at the beginning | *O(k)* |
|
|
102
|
-
| `appendValuesBack(values)` | add values as a list at the end | *O(k)* |
|
|
103
|
-
|
|
104
|
-
Value node (`List.ValueNode`) methods are:
|
|
105
|
-
|
|
106
|
-
| Method | Description | Complexity |
|
|
107
|
-
|------|-----------|-----|
|
|
108
|
-
| `pop()` | remove and return the value | *O(1)* |
|
|
109
|
-
| `addBefore(value)` | add a new value before the current node | *O(1)* |
|
|
110
|
-
| `addAfter(value)` | add a new value after the current node | *O(1)* |
|
|
111
|
-
| `insertBefore(list)` | insert a list before the current node | *O(1)* |
|
|
112
|
-
| `insertAfter(list)` | insert a list after the current node | *O(1)* |
|
|
113
|
-
|
|
114
|
-
Stand-alone methods for nodes (`List.Node`) are:
|
|
115
|
-
|
|
116
|
-
| Method | Description | Complexity |
|
|
117
|
-
|------|-----------|-----|
|
|
118
|
-
| `List.pop(node)` | remove the node from its list and return `{node, list}` | *O(1)* |
|
|
119
|
-
| `List.extract(from, to)` | remove nodes from their list and return the first node of the extracted list | *O(1)* |
|
|
120
|
-
| `List.splice(head1, head2)` | combine two lists and return the first node of the combined list | *O(1)* |
|
|
121
|
-
|
|
122
|
-
### ListHead
|
|
123
|
-
|
|
124
|
-
`ListHead` implements a circular doubly linked list. Its head is an object with the following properties:
|
|
12
|
+
* Converters for `null`-terminated (NT) lists, both singly and doubly linked. They convert in place from NT lists to circular lists and back.
|
|
13
|
+
* Various lists:
|
|
14
|
+
* Doubly linked circular lists (DLL) and singly linked circular lists (SLL).
|
|
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
|
+
* 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
|
+
* Heaps:
|
|
18
|
+
* Min-heap to support priority queues.
|
|
19
|
+
* Various list-based data structures:
|
|
20
|
+
* Caches with various eviction algorithms: least recently used (LRU), least frequently used (LFU), first in first out (FIFO), and random.
|
|
21
|
+
* A decorator is provided to decorate functions, methods, and getters with a cache of your choice.
|
|
22
|
+
* Queue: an adapter for lists.
|
|
23
|
+
* Numerous list utilities.
|
|
125
24
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
* `head`: the head node of a circular doubly linked list. Default: `{}`.
|
|
25
|
+
All lists can be used without the toolkit. Your existing lists, either doubly or singly linked,
|
|
26
|
+
can be used. The toolkit provides a few utilities that you would write yourself if you wanted to use them.
|
|
129
27
|
|
|
130
|
-
|
|
131
|
-
objects are modified in-place by adding the properties defined by `nextName` and `prevName`.
|
|
28
|
+
The implementation philosophy was very simple:
|
|
132
29
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
30
|
+
* Flexibility, efficiency, and simplicity.
|
|
31
|
+
* No dependencies. No unexpected surprises.
|
|
32
|
+
* You never pay for what you don't use.
|
|
33
|
+
* Suitable for all environments.
|
|
34
|
+
* Should be usable with already existing lists.
|
|
35
|
+
* Could be used as a foundation for other list-based data structures.
|
|
136
36
|
|
|
137
|
-
|
|
138
|
-
different nodes of the same list. In fact, creating a new `ListHead` instance is cheap and can be done
|
|
139
|
-
when needed using any node/value.
|
|
37
|
+
**Read all about the implemented ideas in the [Backgrounders](./Backgrounder).**
|
|
140
38
|
|
|
141
|
-
|
|
142
|
-
Alternatively a `ListHead` can be created by pointing to an existing node. In this case,
|
|
143
|
-
any valid node can be used as a head.
|
|
39
|
+
All lists support similar intuitive interfaces:
|
|
144
40
|
|
|
145
|
-
|
|
146
|
-
|
|
41
|
+
* Creating from existing objects.
|
|
42
|
+
* Adding, inserting, extracting and removing nodes.
|
|
43
|
+
* Forward and reverse iterators.
|
|
44
|
+
* General manipulations like reversing and sorting.
|
|
45
|
+
* Link names for the next and previous links (for doubly linked lists) are customizable.
|
|
147
46
|
|
|
148
|
-
|
|
149
|
-
import ListHead from 'list-toolkit/ListHead.js';
|
|
150
|
-
// or
|
|
151
|
-
// const ListHead = require('list-toolkit/ListHead.js').default; // CJS
|
|
152
|
-
|
|
153
|
-
const list1 = new ListHead();
|
|
154
|
-
const list2 = new ListHead(null, {nextName: Symbol('next'), prevName:Symbol('prev')});
|
|
47
|
+
All facilities are efficient, well-debugged, and battle-tested.
|
|
155
48
|
|
|
156
|
-
|
|
49
|
+
**All documentation is in the [wiki](https://github.com/uhop/list-toolkit/wiki).**
|
|
157
50
|
|
|
158
|
-
|
|
159
|
-
list2.push(value);
|
|
51
|
+
## Installation
|
|
160
52
|
|
|
161
|
-
|
|
162
|
-
|
|
53
|
+
```bash
|
|
54
|
+
npm install list-toolkit
|
|
163
55
|
```
|
|
164
56
|
|
|
165
|
-
|
|
166
|
-
The differences are:
|
|
167
|
-
|
|
168
|
-
| Method | Description | Complexity |
|
|
169
|
-
|------|-----------|-----|
|
|
170
|
-
| `new ListHead(head = null, {nextName = 'next', prevName = 'prev'})` | create a new List optionally adopting a list by `head` | *O(1)* |
|
|
171
|
-
| `ListHead.from(values, next, prev)` | create a new List from an iterable | *O(k)* |
|
|
172
|
-
| `ListHead.pop({nextName, prevName}, node)` | remove the node from its list and return `{node, list}` | *O(1)* |
|
|
173
|
-
| `ListHead.extract({nextName, prevName}, from, to)` | remove nodes from their list and return the first node of the extracted list | *O(1)* |
|
|
174
|
-
| `ListHead.splice({nextName, prevName}, head1, head2)` | combine two lists and return the first node of the combined list | *O(1)* |
|
|
175
|
-
| `makeNode()` | return an otherwise empty object with proper `next` and `prev` properties as circular doubly linked list node | *O(1)* |
|
|
176
|
-
| `adopt(node)` | make sure that a node is already a circular doubly linked list or make it so | *O(1)* |
|
|
177
|
-
| `makeList(head)` | return a new list pointing to `head` with the same `nextName` and `prevName` properties as this list | *O(1)* |
|
|
178
|
-
| `clear(true)` | remove all elements and breaks circular references | *O(n)* |
|
|
179
|
-
| `clear()` | remove all elements | *O(1)* |
|
|
180
|
-
|
|
181
|
-
`ListHead` defines a helper class `ListHead.Unsafe` that can be used to create lists without checking
|
|
182
|
-
their `nextName` and `prevName` properties:
|
|
57
|
+
## Introduction
|
|
183
58
|
|
|
184
|
-
|
|
185
|
-
const item1 = {a: 1}, item2 = {b: 2};
|
|
186
|
-
item1.next = item2;
|
|
187
|
-
item2.prev = item1;
|
|
188
|
-
item1.prev = item2;
|
|
189
|
-
item2.next = item1;
|
|
190
|
-
|
|
191
|
-
const list = new ListHead(new ListHead.Unsafe(item1));
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
### SList
|
|
59
|
+
The full documentation is available in the project's [wiki](https://github.com/uhop/list-toolkit/wiki). Below is a cheat sheet of the API.
|
|
195
60
|
|
|
196
|
-
|
|
197
|
-
Other nodes of the list are value nodes (`SList.ValueNode`) that have a `value` property.
|
|
61
|
+
Value lists are containers for arbitrary values:
|
|
198
62
|
|
|
199
63
|
```js
|
|
200
|
-
import
|
|
201
|
-
// or
|
|
202
|
-
// const SList = require('list-toolkit/SList.js').default; // CJS
|
|
64
|
+
import ValueList from 'list-toolkit/value-list.js';
|
|
203
65
|
|
|
204
|
-
const
|
|
205
|
-
const list2 = SList.from([1, 2, 3]);
|
|
206
|
-
```
|
|
66
|
+
const list = ValueList.from([1, 2, 3]);
|
|
207
67
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
Note that for efficiency reasons some methods accept a previous node as a pointer to a required node.
|
|
214
|
-
|
|
215
|
-
Main operations are:
|
|
216
|
-
|
|
217
|
-
| Method | Description | Complexity |
|
|
218
|
-
|------|-----------|-----|
|
|
219
|
-
| `new SList()` | create a new List | *O(1)* |
|
|
220
|
-
| `SList.from(values)` | create a new List from an iterable or an array | *O(k)* |
|
|
221
|
-
| `isEmpty` | check if the list is empty | *O(1)* |
|
|
222
|
-
| `front` | get the first element | *O(1)* |
|
|
223
|
-
| `getBack()` | get the last element | *O(n)* |
|
|
224
|
-
| `getLength()` | get the length of the list | *O(n)* |
|
|
225
|
-
| `getPtr()` | get a special pointer value (see below) | *O(1)* |
|
|
226
|
-
| `popFront()` | remove and return the first element | *O(1)* |
|
|
227
|
-
| `popBack()` | remove and return the last element | *O(n)* |
|
|
228
|
-
| `pushFront(value)` | add a new element at the beginning | *O(1)* |
|
|
229
|
-
| `pushBack(value)` | add a new element at the end | *O(n)* |
|
|
230
|
-
| `appendFront(list)` | add a list at the beginning | *O(nk* |
|
|
231
|
-
| `appendBack(list)` | add a list at the end | *O(n + k)* |
|
|
232
|
-
| `moveToFront(node)` | move a node to the front | *O(n)* |
|
|
233
|
-
| `moveToFront(ptr)` | move a node by pointer (see below) to the front | *O(1)* |
|
|
234
|
-
| `moveToBack(node)` | move a node to the back | *O(n)* |
|
|
235
|
-
| `moveToBack(ptr)` | move a node by pointer (see below) to the back | *O(1)* |
|
|
236
|
-
| `clear()` | remove all elements | *O(1)* |
|
|
237
|
-
| `remove(from, to = from)` | remove a range of elements | *O(n)* |
|
|
238
|
-
| `remove(fromPtr, to = from)` | remove a range of elements using a pointer (see below) | *O(1)* |
|
|
239
|
-
| `extract(from, to)` | remove and return a range of elements as a new list | *O(n)* |
|
|
240
|
-
| `extract(fromPtr, to)` | remove and return a range of elements as a new list using a pointer (see below) | *O(1)* |
|
|
241
|
-
| `reverse()` | reverse the list inline | *O(n)* |
|
|
242
|
-
| `sort(compareFn)` | sort the list inline | *O(n * log(n))* |
|
|
243
|
-
|
|
244
|
-
Useful aliases are:
|
|
245
|
-
|
|
246
|
-
| Method | Alias of |
|
|
247
|
-
|------|-----------|
|
|
248
|
-
| `pop()` | `popFront()` |
|
|
249
|
-
| `push()` | `pushFront()` |
|
|
250
|
-
| `append()` | `appendBack()` |
|
|
251
|
-
|
|
252
|
-
List can be used as an iterable:
|
|
253
|
-
|
|
254
|
-
```js
|
|
255
|
-
const list = SList.from([1, 2, 3]);
|
|
68
|
+
// iterate over the list manually
|
|
69
|
+
for (let node = list.front; node !== list; node = node.next) {
|
|
70
|
+
console.log(node.value); // 1, 2, 3
|
|
71
|
+
}
|
|
256
72
|
|
|
73
|
+
// iterate over the list with an iterator
|
|
257
74
|
for (const value of list) {
|
|
258
|
-
console.log(value);
|
|
75
|
+
console.log(value); // 1, 2, 3
|
|
259
76
|
}
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
Additional iterator-related methods are:
|
|
263
|
-
|
|
264
|
-
| Method | Description | Complexity |
|
|
265
|
-
|------|-----------|-----|
|
|
266
|
-
| `getIterable(from, to)` | get an iterable of a range | *O(1)* |
|
|
267
|
-
| `getPtrIterable(from, to)` | get an iterable of a range using a pointer (see below) | *O(n)* |
|
|
268
|
-
| `getPtrIterable(fromPtr, to)` | get an iterable of a range using a pointer (see below) | *O(1)* |
|
|
269
|
-
|
|
270
|
-
Helper methods are:
|
|
271
|
-
|
|
272
|
-
| Method | Description | Complexity |
|
|
273
|
-
|------|-----------|-----|
|
|
274
|
-
| `makeFrom(values)` | (a meta helper) create a new list from an iterable or an array | *O(k)* |
|
|
275
|
-
| `pushValuesFront(values)` | add values at the beginning | *O(k)* |
|
|
276
|
-
| `appendValuesFront(values)` | add values as a list at the beginning | *O(k)* |
|
|
277
|
-
|
|
278
|
-
Value node (`SList.ValueNode`) methods are:
|
|
279
|
-
|
|
280
|
-
| Method | Description | Complexity |
|
|
281
|
-
|------|-----------|-----|
|
|
282
|
-
| `addAfter(value)` | add a new value after the current node | *O(1)* |
|
|
283
|
-
| `insertAfter(list)` | insert a list after the current node | *O(k)* |
|
|
284
|
-
|
|
285
|
-
Stand-alone methods for nodes (`SList.Node`) are:
|
|
286
77
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
| `SList.extract(prevFrom, nodeTo)` | remove nodes from their list and return `{prev, node}`, where `node` is the first node of the extracted list, and `prev` is the previous node | *O(1)* |
|
|
291
|
-
| `SList.splice(prev1, {prev, node})` | combine two lists and return the first node of the combined list | *O(1)* |
|
|
292
|
-
| `SList.getPrev(list, node)` | get a previous node of the given node | *O(n)* |
|
|
293
|
-
|
|
294
|
-
`SList` provides a special pointer: `SList.SListPtr`. It can be used to create a pointer to a node in a list. It is constructed using a previous node, which makes some operations more efficient.
|
|
295
|
-
|
|
296
|
-
Its methods are:
|
|
297
|
-
|
|
298
|
-
| Method | Description | Complexity |
|
|
299
|
-
|------|-----------|-----|
|
|
300
|
-
| `new SList.SListPtr(list, prev = list)` | create a new pointer | *O(1)* |
|
|
301
|
-
| `list` | the head node | *O(1)* |
|
|
302
|
-
| `prev` | the previous node | *O(1)* |
|
|
303
|
-
| `isHead` | check if the pointer is at the head of the list | *O(1)* |
|
|
304
|
-
| `next()` | move the pointer to the next node | *O(1)* |
|
|
305
|
-
| `clone()` | make a copy of the pointer | *O(1)* |
|
|
306
|
-
|
|
307
|
-
Every method of `SList` that accepts a node can accept a pointer too.
|
|
308
|
-
Using pointers is frequently more efficient than using nodes directly.
|
|
309
|
-
|
|
310
|
-
### SListHead
|
|
311
|
-
|
|
312
|
-
`SListHead` is modelled after `ListHead` but it is specialized for `SList` instances.
|
|
313
|
-
Just like `ListHead`, it works with naked objects.
|
|
314
|
-
|
|
315
|
-
`SListHead` implements a circular singly linked list. Its head is an object with the following properties:
|
|
316
|
-
|
|
317
|
-
* `nextName`: the property name of the next node. It can be a string or a symbol. Default: `"next"`.
|
|
318
|
-
* `head`: the head node of a circular singly linked list. Default: `{}`.
|
|
319
|
-
|
|
320
|
-
All notes related to `ListHead` apply here too.
|
|
321
|
-
|
|
322
|
-
```js
|
|
323
|
-
import SListHead from 'list-toolkit/SListHead.js';
|
|
324
|
-
// or
|
|
325
|
-
// const SListHead = require('list-toolkit/SListHead.js').default; // CJS
|
|
78
|
+
// add more values:
|
|
79
|
+
list.pushBack(4);
|
|
80
|
+
list.pushFront(0);
|
|
326
81
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const value = {a: 1};
|
|
331
|
-
|
|
332
|
-
list1.push(value);
|
|
333
|
-
list2.push(value);
|
|
334
|
-
|
|
335
|
-
SListHead.pop({nextName: 'next'}, value);
|
|
336
|
-
SListHead.pop(list2, value);
|
|
82
|
+
console.log(list.popFront().value); // 0
|
|
83
|
+
console.log(list.front.value); // 1
|
|
337
84
|
```
|
|
338
85
|
|
|
339
|
-
|
|
340
|
-
The differences are:
|
|
341
|
-
|
|
342
|
-
| Method | Description | Complexity |
|
|
343
|
-
|------|-----------|-----|
|
|
344
|
-
| `new SListHead(head = null, next = 'next')` | create a new `SListHead` optionally adopting a list by `head` | *O(1)* |
|
|
345
|
-
| `SListHead.from(values, next)` | create a new `SListHead` from an iterable | *O(k)* |
|
|
346
|
-
| `SListHead.pop({nextName}, prev)` | remove the node by its previous node from its list and return `{node, list}` | *O(1)* |
|
|
347
|
-
| `SListHead.extract({nextName}, prevFrom, nodeTo)` | remove nodes from its list and return `{prev, node}` | *O(1)* |
|
|
348
|
-
| `SListHead.splice({nextName}, prev1, {prev, node})` | combine two lists and return the first node of the combined list | *O(1)* |
|
|
349
|
-
| `SListHead.getPrev({nextName}, list, node)` | get a previous node of the given node | *O(n)* |
|
|
350
|
-
| `make(newHead = null)` | return a new `SListHead` pointing to `newHead` with the same `nextName` property as this list | *O(1)* |
|
|
351
|
-
| `makeFrom(values)` | (a meta helper) create a new `SListHead` from an iterable with the same `nextName` property as this list | *O(k)* |
|
|
352
|
-
| `clear(true)` | remove all elements and breaks circular references | *O(n)* |
|
|
353
|
-
| `clear()` | remove all elements | *O(1)* |
|
|
354
|
-
|
|
355
|
-
`SListHead.SListPtr` is a pointer-like class similar to `SList.SListPtr`
|
|
356
|
-
but specialized for `SListHead` instances. It has the same API and the same semantics.
|
|
357
|
-
|
|
358
|
-
### Cache
|
|
359
|
-
|
|
360
|
-
This is the class that is used for caching values using simple unique keys (numbers, strings or symbols).
|
|
361
|
-
It defines a capacity and when it is full, it removes the least recently used value.
|
|
362
|
-
|
|
363
|
-
Internally it is based on `List`.
|
|
86
|
+
Lists can be made of arbitrary objects:
|
|
364
87
|
|
|
365
88
|
```js
|
|
366
|
-
import
|
|
367
|
-
// or
|
|
368
|
-
// const Cache = require('list-toolkit/Cache.js').default; // CJS
|
|
369
|
-
|
|
370
|
-
const cache = new Cache(1000);
|
|
89
|
+
import List from 'list-toolkit/list.js';
|
|
371
90
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
data = new DataObject();
|
|
376
|
-
cache.register(key, data);
|
|
91
|
+
class Person {
|
|
92
|
+
constructor(name) {
|
|
93
|
+
this.name = name;
|
|
377
94
|
}
|
|
378
|
-
// continue processing
|
|
379
95
|
};
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
The main operations are:
|
|
383
96
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
| `capacity` | get the capacity of the cache | *O(1)* |
|
|
389
|
-
| `find(key)` | find a value by its key and return it or `undefined` | *O(1)* |
|
|
390
|
-
| `remove(key)` | remove a value by its key | *O(1)* |
|
|
391
|
-
| `register(key, value)` | register a value by its key | *O(1)* |
|
|
392
|
-
| `clear()` | clear the cache | *O(1)* |
|
|
393
|
-
| `getReverseIterable()` | get an iterable of the cache in reverse order | *O(1)* |
|
|
97
|
+
const john = new Person('John'),
|
|
98
|
+
jane = new Person('Jane'),
|
|
99
|
+
jim = new Person('Jim'),
|
|
100
|
+
jill = new Person('Jill');
|
|
394
101
|
|
|
395
|
-
|
|
102
|
+
const people = List.from([john, jane, jim, jill]);
|
|
396
103
|
|
|
397
|
-
|
|
398
|
-
for (
|
|
399
|
-
console.log(
|
|
104
|
+
// iterator over the list manually:
|
|
105
|
+
for (let node = people.front; node !== people; node = node[list.nextName]) {
|
|
106
|
+
console.log(node.name); // John, Jane, Jim, Jill
|
|
400
107
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
It iterates from the most recently used to the least recently used.
|
|
108
|
+
// yes, the link names are customizable, can be strings or symbols, for example:
|
|
404
109
|
|
|
405
|
-
|
|
110
|
+
const ladies = List.from([jane, jill], {nextName: 'n', prevName: 'p'});
|
|
406
111
|
|
|
407
|
-
|
|
112
|
+
// iterate over ladies
|
|
113
|
+
for (let node = ladies.front; node !== ladies; node = node.n) {
|
|
114
|
+
console.log(node.name); // Jane, Jill
|
|
115
|
+
}
|
|
408
116
|
|
|
409
|
-
|
|
117
|
+
// let's move Jim to the front and John to the back:
|
|
118
|
+
people.moveToFront(jim);
|
|
119
|
+
people.moveToBack(john);
|
|
410
120
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
// or
|
|
414
|
-
// const MinHeap = require('list-toolkit/MinHeap.js').default; // CJS
|
|
121
|
+
// sort the list
|
|
122
|
+
people.sort((a, b) => a.name.localeCompare(b.name) < 0);
|
|
415
123
|
|
|
416
|
-
const
|
|
124
|
+
for (const node of people) {
|
|
125
|
+
console.log(node.name); // Jane, Jill, Jim, John
|
|
126
|
+
}
|
|
417
127
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
128
|
+
// let's extract all people from Jill to Jim
|
|
129
|
+
const ji = people.extract({from: jill, to: jim});
|
|
130
|
+
for (const node of people) console.log(node.name); // Jane, John
|
|
131
|
+
for (const node of ji) console.log(node.name); // Jim, Jill
|
|
421
132
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}
|
|
133
|
+
// add them back:
|
|
134
|
+
people.append(ji);
|
|
135
|
+
for (const node of people.getReverseIterator()) {
|
|
136
|
+
console.log(node.name); // Jill, Jim, John, Jane
|
|
427
137
|
}
|
|
428
|
-
|
|
138
|
+
ji.isEmpty === true;
|
|
429
139
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
| Method | Description | Complexity |
|
|
433
|
-
|------|-----------|-----|
|
|
434
|
-
| `new MinHeap({less = (a, b) => a < b, equal: (a, b) => a === b}, ...args)` | create a new heap optionally from iterables or other heaps | *O(k)* |
|
|
435
|
-
| `length` | get the number of elements in the heap | *O(1)* |
|
|
436
|
-
| `isEmpty` | check if the heap is empty | *O(1)* |
|
|
437
|
-
| `top` | get the top element | *O(1)* |
|
|
438
|
-
| `clear()` | clear the heap | *O(1)* |
|
|
439
|
-
| `pop()` | remove and return the top element | *O(log(n))* |
|
|
440
|
-
| `push(value)` | add new element | *O(log(n))* |
|
|
441
|
-
| `pushPop(value)` | add new element and then return the top element | *O(log(n))* |
|
|
442
|
-
| `replaceTop(value)` | return the top element and then add new element | *O(log(n))* |
|
|
443
|
-
| `releaseSorted()` | remove all elements and return them as an array in the reverse sorted order (the heap will be cleared) | *O(n)* |
|
|
444
|
-
| `merge(...args)` | add elements from iterables or other heaps | *O(k)* |
|
|
445
|
-
| `make(...args)` | return a new heap with the same options | *O(1)* |
|
|
446
|
-
| `clone()` | return a copy of the heap | *O(n)* |
|
|
447
|
-
|
|
448
|
-
`MinHeap` provides a number of static methods to create heaps on arrays (used internally):
|
|
449
|
-
|
|
450
|
-
| Method | Description | Complexity |
|
|
451
|
-
|------|-----------|-----|
|
|
452
|
-
| `MinHeap.build(array, less = (a, b) => a < b)` | create a new heap from an array | *O(n)* |
|
|
453
|
-
| `MinHeap.pop(heapArray, less = (a, b) => a < b)` | remove and return the top element | *O(log(n))* |
|
|
454
|
-
| `MinHeap.push(heapArray, value, less = (a, b) => a < b)` | add new element | *O(log(n))* |
|
|
455
|
-
| `MinHeap.pushPop(heapArray, value, less = (a, b) => a < b)` | add new element and then return the top element | *O(log(n))* |
|
|
456
|
-
| `MinHeap.replaceTop(heapArray, value, less = (a, b) => a < b)` | return the top element and then add new element | *O(log(n))* |
|
|
457
|
-
| `MinHeap.sort(heapArray, less = (a, b) => a < b)` | sort an array in place | *O(n * log(n))* |
|
|
140
|
+
// BTW, the list `ladies` is unchanged
|
|
141
|
+
```
|
|
458
142
|
|
|
459
143
|
## License
|
|
460
144
|
|
|
@@ -462,4 +146,6 @@ BSD 3-Clause "New" or "Revised" License. See the LICENSE file for details.
|
|
|
462
146
|
|
|
463
147
|
## Release History
|
|
464
148
|
|
|
149
|
+
* 2.0.0 *New major release.*
|
|
150
|
+
* 1.0.1 *Fixed exports. Added more methods to `MinHeap`.*
|
|
465
151
|
* 1.0.1 *Initial release.*
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.CacheFIFO = void 0;
|
|
7
|
+
var _cacheLru = _interopRequireDefault(require("./cache-lru.js"));
|
|
8
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
// Evicts on the first-in-first-out basis.
|
|
10
|
+
|
|
11
|
+
class CacheFIFO extends _cacheLru.default {
|
|
12
|
+
use(key) {
|
|
13
|
+
return this.dict.get(key);
|
|
14
|
+
}
|
|
15
|
+
addNew(key, value) {
|
|
16
|
+
this.list.pushBack({
|
|
17
|
+
key,
|
|
18
|
+
value
|
|
19
|
+
});
|
|
20
|
+
const node = this.list.back;
|
|
21
|
+
this.dict.set(key, node);
|
|
22
|
+
return node;
|
|
23
|
+
}
|
|
24
|
+
evictAndReplace(key, value) {
|
|
25
|
+
const node = this.list.front;
|
|
26
|
+
this.list.moveToBack(node);
|
|
27
|
+
this.dict.delete(node.value.key);
|
|
28
|
+
this.dict.set(key, node);
|
|
29
|
+
node.value = {
|
|
30
|
+
key,
|
|
31
|
+
value
|
|
32
|
+
};
|
|
33
|
+
return node;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.CacheFIFO = CacheFIFO;
|
|
37
|
+
var _default = exports.default = CacheFIFO;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = exports.CacheLFU = void 0;
|
|
7
|
+
var _metaUtils = require("../meta-utils.js");
|
|
8
|
+
var _minHeap = _interopRequireDefault(require("../heap/min-heap.js"));
|
|
9
|
+
var _cacheLru = _interopRequireDefault(require("./cache-lru.js"));
|
|
10
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
// Evicts the least frequently used items.
|
|
12
|
+
|
|
13
|
+
class CacheLFU extends _cacheLru.default {
|
|
14
|
+
constructor(capacity = 10) {
|
|
15
|
+
super(capacity);
|
|
16
|
+
this.heap = new _minHeap.default({
|
|
17
|
+
less: (a, b) => a.value.counter < b.value.counter
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
use(key) {
|
|
21
|
+
const node = this.dict.get(key);
|
|
22
|
+
if (node) ++node.value.counter;
|
|
23
|
+
return node;
|
|
24
|
+
}
|
|
25
|
+
update(node, value) {
|
|
26
|
+
node.value.counter = 1;
|
|
27
|
+
node.value.value = value;
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
addNew(key, value) {
|
|
31
|
+
this.list.pushFront({
|
|
32
|
+
key,
|
|
33
|
+
value,
|
|
34
|
+
counter: 1
|
|
35
|
+
});
|
|
36
|
+
const node = this.list.front;
|
|
37
|
+
this.dict.set(key, node);
|
|
38
|
+
this.heap.push(node);
|
|
39
|
+
return node;
|
|
40
|
+
}
|
|
41
|
+
evictAndReplace(key, value) {
|
|
42
|
+
const node = this.heap.top;
|
|
43
|
+
this.dict.delete(node.value.key);
|
|
44
|
+
node.value = {
|
|
45
|
+
key,
|
|
46
|
+
value,
|
|
47
|
+
counter: 1
|
|
48
|
+
};
|
|
49
|
+
this.dict.set(key, node);
|
|
50
|
+
this.heap.updateTop();
|
|
51
|
+
return node;
|
|
52
|
+
}
|
|
53
|
+
remove(key) {
|
|
54
|
+
const node = this.dict.get(key);
|
|
55
|
+
if (node) {
|
|
56
|
+
this.dict.delete(key);
|
|
57
|
+
this.list.removeNode(node);
|
|
58
|
+
this.heap.remove(node);
|
|
59
|
+
}
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
clear() {
|
|
63
|
+
super.clear();
|
|
64
|
+
this.heap.clear();
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
resetCounters(initialValue = 1) {
|
|
68
|
+
for (const item of this.heap) {
|
|
69
|
+
item.counter = initialValue;
|
|
70
|
+
}
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.CacheLFU = CacheLFU;
|
|
75
|
+
(0, _metaUtils.addAlias)(CacheLFU.prototype, 'remove', 'delete');
|
|
76
|
+
var _default = exports.default = CacheLFU;
|