@llblab/uniqueue 1.2.1 → 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 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, // Unique by playerId
38
- maxSize: 3, // Keep only top 3 scores
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
- console.log(leaderboard.data);
60
+ leaderboard.snapshot();
57
61
  // Contains charlie (120), alice (150), dave (200)
58
62
 
59
63
  // Iterate over all items
@@ -75,7 +79,7 @@ Creates a new priority queue instance.
75
79
  | `data` | `T[]` | `[]` | Initial data array. |
76
80
  | `maxSize` | `number` | `Infinity` | Maximum number of items. If exceeded, lowest priority item is evicted. |
77
81
  | `compare` | `(a, b) => number` | `(a, b) => (a < b ? -1 : a > b ? 1 : 0)` | Comparison function for heap ordering. |
78
- | `extractKey` | `(item) => string` | `(item) => item` | Function to extract unique key string from item. |
82
+ | `extractKey` | `(item) => K` | `(item) => item` | Function to extract unique key any type from item. |
79
83
 
80
84
  ### Instance Methods
81
85
 
@@ -94,15 +98,15 @@ Removes and returns the highest priority item (the root of the heap).
94
98
 
95
99
  Returns the highest priority item without removing it.
96
100
 
97
- #### `remove(key: string): boolean`
101
+ #### `remove(key: K): boolean`
98
102
 
99
103
  Removes the item with the given key. Returns `true` if removed, `false` otherwise.
100
104
 
101
- #### `has(key: string): boolean`
105
+ #### `has(key: K): boolean`
102
106
 
103
107
  Checks if an item with the given key exists.
104
108
 
105
- #### `get(key: string): T | undefined`
109
+ #### `get(key: K): T | undefined`
106
110
 
107
111
  Returns the item with the given key without removing it.
108
112
 
@@ -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
@@ -1,22 +1,21 @@
1
- export interface UniqueueOptions<T> {
1
+ export interface UniqueueOptions<T, K = string> {
2
2
  data?: T[];
3
3
  maxSize?: number;
4
4
  compare?: (a: T, b: T) => number;
5
- extractKey?: (item: T) => string;
5
+ extractKey?: (item: T) => K;
6
6
  }
7
- export declare class Uniqueue<T> {
7
+ export declare class Uniqueue<T, K = string> {
8
8
  #private;
9
- data: T[];
10
- indexes: Map<string, number>;
11
- constructor({ data, maxSize, compare, extractKey, }?: UniqueueOptions<T>);
9
+ constructor({ data, maxSize, compare, extractKey, }?: UniqueueOptions<T, K>);
12
10
  push(item: T): T | undefined;
13
11
  pop(): T | undefined;
14
12
  peek(): T | undefined;
15
- remove(key: string): boolean;
16
- has(key: string): boolean;
17
- get(key: string): T | undefined;
13
+ remove(key: K): boolean;
14
+ has(key: K): boolean;
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,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,MAAM,CAAC;CAClC;AAED,qBAAa,QAAQ,CAAC,CAAC;;IACrB,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAMjB,EACV,IAAS,EACT,OAAkB,EAClB,OAAgD,EAChD,UAAgD,GACjD,GAAE,eAAe,CAAC,CAAC,CAAM;IAkE1B,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,MAAM,GAAG,OAAO;IA0B5B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAK/B,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,IAAI,MAAM,CAEjB;IAEA,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAC,CAAC,CAAC;CAG1C"}
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,23 +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 = data;
9
- this.indexes = new Map(data.map((item, index) => [extractKey(item), index]));
8
+ this.#data = [];
9
+ this.#indexes = new Map();
10
10
  this.#maxSize = maxSize;
11
11
  this.#compare = compare;
12
12
  this.#extractKey = extractKey;
13
- if (data.length > 0) {
14
- for (let i = (data.length >>> 1) - 1; i >= 0; i--) {
13
+ for (const item of data) {
14
+ const key = extractKey(item);
15
+ if (!this.#indexes.has(key)) {
16
+ this.#indexes.set(key, this.#data.length);
17
+ this.#data.push(item);
18
+ }
19
+ }
20
+ if (this.#data.length > 0) {
21
+ for (let i = (this.#data.length >>> 1) - 1; i >= 0; i--) {
15
22
  this.#siftDown(i);
16
23
  }
17
24
  }
18
25
  }
19
26
  #siftUp(pos) {
20
- const { data, indexes } = this;
27
+ const data = this.#data;
28
+ const indexes = this.#indexes;
21
29
  const compare = this.#compare;
22
30
  const extractKey = this.#extractKey;
23
31
  const item = data[pos];
@@ -34,7 +42,8 @@ export class Uniqueue {
34
42
  data[pos] = item;
35
43
  }
36
44
  #siftDown(pos) {
37
- const { data, indexes } = this;
45
+ const data = this.#data;
46
+ const indexes = this.#indexes;
38
47
  const compare = this.#compare;
39
48
  const extractKey = this.#extractKey;
40
49
  const item = data[pos];
@@ -61,16 +70,16 @@ export class Uniqueue {
61
70
  }
62
71
  push(item) {
63
72
  const key = this.#extractKey(item);
64
- const index = this.indexes.get(key);
73
+ const index = this.#indexes.get(key);
65
74
  if (index === undefined) {
66
- this.data.push(item);
67
- this.#siftUp(this.data.length - 1);
68
- if (this.data.length <= this.#maxSize)
75
+ this.#data.push(item);
76
+ this.#siftUp(this.#data.length - 1);
77
+ if (this.#data.length <= this.#maxSize)
69
78
  return;
70
79
  return this.pop();
71
80
  }
72
- const oldItem = this.data[index];
73
- this.data[index] = item;
81
+ const oldItem = this.#data[index];
82
+ this.#data[index] = item;
74
83
  const cmp = this.#compare(oldItem, item);
75
84
  if (cmp < 0) {
76
85
  this.#siftDown(index);
@@ -80,37 +89,37 @@ export class Uniqueue {
80
89
  }
81
90
  }
82
91
  pop() {
83
- if (this.data.length === 0)
92
+ if (this.#data.length === 0)
84
93
  return;
85
- const top = this.data[0];
86
- this.indexes.delete(this.#extractKey(top));
87
- const bottom = this.data.pop();
88
- if (this.data.length > 0 && bottom !== undefined) {
89
- this.indexes.set(this.#extractKey(bottom), 0);
90
- this.data[0] = bottom;
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;
91
100
  this.#siftDown(0);
92
101
  }
93
102
  return top;
94
103
  }
95
104
  peek() {
96
- return this.data[0];
105
+ return this.#data[0];
97
106
  }
98
107
  remove(key) {
99
- const index = this.indexes.get(key);
108
+ const index = this.#indexes.get(key);
100
109
  if (index === undefined)
101
110
  return false;
102
- const lastIndex = this.data.length - 1;
111
+ const lastIndex = this.#data.length - 1;
103
112
  if (index === lastIndex) {
104
- this.indexes.delete(key);
105
- this.data.pop();
113
+ this.#indexes.delete(key);
114
+ this.#data.pop();
106
115
  return true;
107
116
  }
108
- const item = this.data.pop();
109
- this.indexes.delete(key);
110
- this.indexes.set(this.#extractKey(item), index);
111
- this.data[index] = item;
117
+ const item = this.#data.pop();
118
+ this.#indexes.delete(key);
119
+ this.#indexes.set(this.#extractKey(item), index);
120
+ this.#data[index] = item;
112
121
  const parentIndex = (index - 1) >>> 1;
113
- if (index > 0 && this.#compare(item, this.data[parentIndex]) < 0) {
122
+ if (index > 0 && this.#compare(item, this.#data[parentIndex]) < 0) {
114
123
  this.#siftUp(index);
115
124
  }
116
125
  else {
@@ -119,20 +128,23 @@ export class Uniqueue {
119
128
  return true;
120
129
  }
121
130
  has(key) {
122
- return this.indexes.has(key);
131
+ return this.#indexes.has(key);
123
132
  }
124
133
  get(key) {
125
- const index = this.indexes.get(key);
126
- return index !== undefined ? this.data[index] : undefined;
134
+ const index = this.#indexes.get(key);
135
+ return index !== undefined ? this.#data[index] : undefined;
127
136
  }
128
137
  clear() {
129
- this.data = [];
130
- this.indexes.clear();
138
+ this.#data = [];
139
+ this.#indexes.clear();
131
140
  }
132
141
  get size() {
133
- return this.data.length;
142
+ return this.#data.length;
143
+ }
144
+ snapshot() {
145
+ return [...this.#data];
134
146
  }
135
147
  *[Symbol.iterator]() {
136
- yield* this.data;
148
+ yield* this.#data;
137
149
  }
138
150
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/uniqueue",
3
- "version": "1.2.1",
3
+ "version": "1.4.0",
4
4
  "description": "High-performance priority queue with unique key constraint (O(1) lookup, O(log n) update)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -28,7 +28,6 @@
28
28
  "deduplication",
29
29
  "leaderboard",
30
30
  "cache",
31
- "lru",
32
31
  "performance"
33
32
  ],
34
33
  "author": "LLB <shlavik@gmail.com>",