@llblab/uniqueue 1.3.0 → 1.4.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 +11 -3
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +46 -41
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,6 +19,10 @@ Combines a binary heap with a key-to-index `Map`, enabling O(log n) inserts/upda
|
|
|
19
19
|
npm install @llblab/uniqueue
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
```bash
|
|
23
|
+
deno add jsr:@llblab/uniqueue
|
|
24
|
+
```
|
|
25
|
+
|
|
22
26
|
## Usage
|
|
23
27
|
|
|
24
28
|
### Basic Example (Top-N Leaderboard)
|
|
@@ -34,8 +38,8 @@ import { Uniqueue } from "@llblab/uniqueue";
|
|
|
34
38
|
// the root (smallest/worst) is evicted, keeping the best scores.
|
|
35
39
|
const leaderboard = new Uniqueue({
|
|
36
40
|
compare: (a, b) => a.score - b.score,
|
|
37
|
-
extractKey: (item) => item.playerId,
|
|
38
|
-
maxSize: 3,
|
|
41
|
+
extractKey: (item) => item.playerId,
|
|
42
|
+
maxSize: 3,
|
|
39
43
|
});
|
|
40
44
|
|
|
41
45
|
// Add items
|
|
@@ -53,7 +57,7 @@ leaderboard.push({ playerId: "alice", score: 150 });
|
|
|
53
57
|
leaderboard.push({ playerId: "dave", score: 200 });
|
|
54
58
|
// Bob (lowest score at root) is evicted
|
|
55
59
|
|
|
56
|
-
|
|
60
|
+
leaderboard.snapshot();
|
|
57
61
|
// Contains charlie (120), alice (150), dave (200)
|
|
58
62
|
|
|
59
63
|
// Iterate over all items
|
|
@@ -114,6 +118,10 @@ Removes all items from the queue.
|
|
|
114
118
|
|
|
115
119
|
Getter property that returns the number of items.
|
|
116
120
|
|
|
121
|
+
#### `snapshot(): T[]`
|
|
122
|
+
|
|
123
|
+
Returns a shallow copy of the underlying heap array. Safe for sorting or other mutations without affecting the queue.
|
|
124
|
+
|
|
117
125
|
#### `[Symbol.iterator](): IterableIterator<T>`
|
|
118
126
|
|
|
119
127
|
Iterates over items in heap order (not sorted).
|
package/dist/index.d.ts
CHANGED
|
@@ -6,8 +6,6 @@ export interface UniqueueOptions<T, K = string> {
|
|
|
6
6
|
}
|
|
7
7
|
export declare class Uniqueue<T, K = string> {
|
|
8
8
|
#private;
|
|
9
|
-
data: T[];
|
|
10
|
-
indexes: Map<K, number>;
|
|
11
9
|
constructor({ data, maxSize, compare, extractKey, }?: UniqueueOptions<T, K>);
|
|
12
10
|
push(item: T): T | undefined;
|
|
13
11
|
pop(): T | undefined;
|
|
@@ -17,6 +15,7 @@ export declare class Uniqueue<T, K = string> {
|
|
|
17
15
|
get(key: K): T | undefined;
|
|
18
16
|
clear(): void;
|
|
19
17
|
get size(): number;
|
|
18
|
+
snapshot(): T[];
|
|
20
19
|
[Symbol.iterator](): IterableIterator<T>;
|
|
21
20
|
}
|
|
22
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM;IAC5C,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;IACjC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC;CAC7B;AAED,qBAAa,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM;IAC5C,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;IACjC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC;CAC7B;AAED,qBAAa,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM;;gBAOrB,EACV,IAAS,EACT,OAAkB,EAClB,OAAgD,EAChD,UAA2C,GAC5C,GAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAM;IA0E7B,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAsB5B,GAAG,IAAI,CAAC,GAAG,SAAS;IAgBpB,IAAI,IAAI,CAAC,GAAG,SAAS;IAIrB,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IA0BvB,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAIpB,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAK1B,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,QAAQ,IAAI,CAAC,EAAE;IAId,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC;CAG1C"}
|
package/dist/index.js
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
export class Uniqueue {
|
|
2
|
-
data;
|
|
3
|
-
indexes;
|
|
2
|
+
#data;
|
|
3
|
+
#indexes;
|
|
4
4
|
#maxSize;
|
|
5
5
|
#compare;
|
|
6
6
|
#extractKey;
|
|
7
7
|
constructor({ data = [], maxSize = Infinity, compare = (a, b) => (a < b ? -1 : a > b ? 1 : 0), extractKey = (item) => item, } = {}) {
|
|
8
|
+
this.#data = [];
|
|
9
|
+
this.#indexes = new Map();
|
|
8
10
|
this.#maxSize = maxSize;
|
|
9
11
|
this.#compare = compare;
|
|
10
12
|
this.#extractKey = extractKey;
|
|
11
|
-
this.data = [];
|
|
12
|
-
this.indexes = new Map();
|
|
13
13
|
for (const item of data) {
|
|
14
14
|
const key = extractKey(item);
|
|
15
|
-
if (!this
|
|
16
|
-
this
|
|
17
|
-
this
|
|
15
|
+
if (!this.#indexes.has(key)) {
|
|
16
|
+
this.#indexes.set(key, this.#data.length);
|
|
17
|
+
this.#data.push(item);
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
-
if (this
|
|
21
|
-
for (let i = (this
|
|
20
|
+
if (this.#data.length > 0) {
|
|
21
|
+
for (let i = (this.#data.length >>> 1) - 1; i >= 0; i--) {
|
|
22
22
|
this.#siftDown(i);
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
#siftUp(pos) {
|
|
27
|
-
const
|
|
27
|
+
const data = this.#data;
|
|
28
|
+
const indexes = this.#indexes;
|
|
28
29
|
const compare = this.#compare;
|
|
29
30
|
const extractKey = this.#extractKey;
|
|
30
31
|
const item = data[pos];
|
|
@@ -41,7 +42,8 @@ export class Uniqueue {
|
|
|
41
42
|
data[pos] = item;
|
|
42
43
|
}
|
|
43
44
|
#siftDown(pos) {
|
|
44
|
-
const
|
|
45
|
+
const data = this.#data;
|
|
46
|
+
const indexes = this.#indexes;
|
|
45
47
|
const compare = this.#compare;
|
|
46
48
|
const extractKey = this.#extractKey;
|
|
47
49
|
const item = data[pos];
|
|
@@ -68,16 +70,16 @@ export class Uniqueue {
|
|
|
68
70
|
}
|
|
69
71
|
push(item) {
|
|
70
72
|
const key = this.#extractKey(item);
|
|
71
|
-
const index = this
|
|
73
|
+
const index = this.#indexes.get(key);
|
|
72
74
|
if (index === undefined) {
|
|
73
|
-
this
|
|
74
|
-
this.#siftUp(this
|
|
75
|
-
if (this
|
|
75
|
+
this.#data.push(item);
|
|
76
|
+
this.#siftUp(this.#data.length - 1);
|
|
77
|
+
if (this.#data.length <= this.#maxSize)
|
|
76
78
|
return;
|
|
77
79
|
return this.pop();
|
|
78
80
|
}
|
|
79
|
-
const oldItem = this
|
|
80
|
-
this
|
|
81
|
+
const oldItem = this.#data[index];
|
|
82
|
+
this.#data[index] = item;
|
|
81
83
|
const cmp = this.#compare(oldItem, item);
|
|
82
84
|
if (cmp < 0) {
|
|
83
85
|
this.#siftDown(index);
|
|
@@ -87,37 +89,37 @@ export class Uniqueue {
|
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
pop() {
|
|
90
|
-
if (this
|
|
92
|
+
if (this.#data.length === 0)
|
|
91
93
|
return;
|
|
92
|
-
const top = this
|
|
93
|
-
this
|
|
94
|
-
const bottom = this
|
|
95
|
-
if (this
|
|
96
|
-
this
|
|
97
|
-
this
|
|
94
|
+
const top = this.#data[0];
|
|
95
|
+
this.#indexes.delete(this.#extractKey(top));
|
|
96
|
+
const bottom = this.#data.pop();
|
|
97
|
+
if (this.#data.length > 0 && bottom !== undefined) {
|
|
98
|
+
this.#indexes.set(this.#extractKey(bottom), 0);
|
|
99
|
+
this.#data[0] = bottom;
|
|
98
100
|
this.#siftDown(0);
|
|
99
101
|
}
|
|
100
102
|
return top;
|
|
101
103
|
}
|
|
102
104
|
peek() {
|
|
103
|
-
return this
|
|
105
|
+
return this.#data[0];
|
|
104
106
|
}
|
|
105
107
|
remove(key) {
|
|
106
|
-
const index = this
|
|
108
|
+
const index = this.#indexes.get(key);
|
|
107
109
|
if (index === undefined)
|
|
108
110
|
return false;
|
|
109
|
-
const lastIndex = this
|
|
111
|
+
const lastIndex = this.#data.length - 1;
|
|
110
112
|
if (index === lastIndex) {
|
|
111
|
-
this
|
|
112
|
-
this
|
|
113
|
+
this.#indexes.delete(key);
|
|
114
|
+
this.#data.pop();
|
|
113
115
|
return true;
|
|
114
116
|
}
|
|
115
|
-
const item = this
|
|
116
|
-
this
|
|
117
|
-
this
|
|
118
|
-
this
|
|
117
|
+
const item = this.#data.pop();
|
|
118
|
+
this.#indexes.delete(key);
|
|
119
|
+
this.#indexes.set(this.#extractKey(item), index);
|
|
120
|
+
this.#data[index] = item;
|
|
119
121
|
const parentIndex = (index - 1) >>> 1;
|
|
120
|
-
if (index > 0 && this.#compare(item, this
|
|
122
|
+
if (index > 0 && this.#compare(item, this.#data[parentIndex]) < 0) {
|
|
121
123
|
this.#siftUp(index);
|
|
122
124
|
}
|
|
123
125
|
else {
|
|
@@ -126,20 +128,23 @@ export class Uniqueue {
|
|
|
126
128
|
return true;
|
|
127
129
|
}
|
|
128
130
|
has(key) {
|
|
129
|
-
return this
|
|
131
|
+
return this.#indexes.has(key);
|
|
130
132
|
}
|
|
131
133
|
get(key) {
|
|
132
|
-
const index = this
|
|
133
|
-
return index !== undefined ? this
|
|
134
|
+
const index = this.#indexes.get(key);
|
|
135
|
+
return index !== undefined ? this.#data[index] : undefined;
|
|
134
136
|
}
|
|
135
137
|
clear() {
|
|
136
|
-
this
|
|
137
|
-
this
|
|
138
|
+
this.#data = [];
|
|
139
|
+
this.#indexes.clear();
|
|
138
140
|
}
|
|
139
141
|
get size() {
|
|
140
|
-
return this
|
|
142
|
+
return this.#data.length;
|
|
143
|
+
}
|
|
144
|
+
snapshot() {
|
|
145
|
+
return [...this.#data];
|
|
141
146
|
}
|
|
142
147
|
*[Symbol.iterator]() {
|
|
143
|
-
yield* this
|
|
148
|
+
yield* this.#data;
|
|
144
149
|
}
|
|
145
150
|
}
|