dubc-ds-tmap 1.0.0 → 1.0.2
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 +269 -1
- package/dist/index.d.ts +14 -13
- package/dist/index.js +91 -46
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -1,3 +1,271 @@
|
|
|
1
1
|
# dubc-ds-tmap
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A map (dictionary) of key/value pairs that is sorted using a weighted
|
|
4
|
+
binary search tree.
|
|
5
|
+
|
|
6
|
+
Note that this data structure maps _arbitrary_ keys to _arbitrary_
|
|
7
|
+
values. When doing front-end development, that's usually not what
|
|
8
|
+
you actually need. Consider using `dubc-ds-tindex` instead, which
|
|
9
|
+
provides a dictionary where the keys are a function of the values.
|
|
10
|
+
|
|
11
|
+
## Constructor
|
|
12
|
+
|
|
13
|
+
```TypeScript
|
|
14
|
+
constructor(conf:Conf<K,V>, pairs:Pairs<K,V>)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
To construct a `TMap`, you need a configuration object that supplies
|
|
18
|
+
three things:
|
|
19
|
+
|
|
20
|
+
1. A `compare` function that determines how to sort the keys in the map.
|
|
21
|
+
2. A `unique` flag that determines whether the map can contain duplicate keys.
|
|
22
|
+
3. A `valueEq` function that determines how values should be compared.
|
|
23
|
+
|
|
24
|
+
You also can supply initial key/value pairs to populate the map. The
|
|
25
|
+
pairs can be specified in three ways:
|
|
26
|
+
|
|
27
|
+
1. You can specify an iterable of objects with `key` and `value` fields;
|
|
28
|
+
2. Or an iterable of key/value tuples, where the first part of the tuple
|
|
29
|
+
is the key and the second part of the tuplie is the value;
|
|
30
|
+
3. Or, if the keys are strings, you can use a plain JavaScript object.
|
|
31
|
+
|
|
32
|
+
If you want unique keys and can use `Object.is` for your `valueEq` function,
|
|
33
|
+
you can use the static `of` method to construct a `TMap` with variadic
|
|
34
|
+
key/value pairs.
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
|
|
38
|
+
```TypeScript
|
|
39
|
+
import { Conf, TMap } from "dubc-ds-tmap"
|
|
40
|
+
|
|
41
|
+
const compare = (a:string, b:string) => a.localeCompare(b)
|
|
42
|
+
|
|
43
|
+
const conf:Conf<string,number> = {
|
|
44
|
+
compare, unique:false, valueEq:Object.is,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// These five statements all do the same thing.
|
|
48
|
+
const map1 = new TMap(conf, [{key:"1", value:1}, {key:"2", value:2}])
|
|
49
|
+
const map2 = new TMap(conf, [["1", 1], ["2", 2]])
|
|
50
|
+
const map3 = new TMap(conf, {"1":1, "2":2})
|
|
51
|
+
const map4 = TMap.of(compare, {key:"1", value:1}, {key:"2", value:2})
|
|
52
|
+
const map5 = TMap.of(compare, ["1", 1], ["2", 2])
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Properties
|
|
56
|
+
|
|
57
|
+
### `.compare` O(1)
|
|
58
|
+
|
|
59
|
+
Returns the current function used to compare keys. This property is
|
|
60
|
+
mutable: If you change it, the map will re-sort (and fire a notification
|
|
61
|
+
to any listeners that the map has changed.)
|
|
62
|
+
|
|
63
|
+
### `.first` O(log n)
|
|
64
|
+
|
|
65
|
+
Returns the first key/value pair in the map, or `undefined` if the map
|
|
66
|
+
is empty.
|
|
67
|
+
|
|
68
|
+
### `.i` O(1)
|
|
69
|
+
|
|
70
|
+
Returns an iterator over the key/value pairs in the map. Each value
|
|
71
|
+
returned by the iterator will be an immutable object in the form `{key:k, value:v}`. (This property is inherited from `Base`.)
|
|
72
|
+
|
|
73
|
+
### `.last` O(log n)
|
|
74
|
+
|
|
75
|
+
Returns the last key/value pair in the map, or `undefined` if the map
|
|
76
|
+
is empty.
|
|
77
|
+
|
|
78
|
+
### `.only` O(1)
|
|
79
|
+
|
|
80
|
+
Returns the only key/value pair in the map. If the map doesn't have
|
|
81
|
+
exactly one element, throws an `Error`.
|
|
82
|
+
|
|
83
|
+
### `.size` O(1)
|
|
84
|
+
|
|
85
|
+
Returns the number of key/value pairs in the map.
|
|
86
|
+
|
|
87
|
+
## Methods
|
|
88
|
+
|
|
89
|
+
### `.after(k:K)` O(log n)
|
|
90
|
+
|
|
91
|
+
Returns the key/value pair whose key is strictly greater than the provided
|
|
92
|
+
key, or `undefined` if no such pair exists in the map.
|
|
93
|
+
|
|
94
|
+
### `.at(index:number)` O(log n)
|
|
95
|
+
|
|
96
|
+
Returns the key/value pair at the specified index in the sort order.
|
|
97
|
+
Throws a `TypeError` if the index is out of bounds.
|
|
98
|
+
|
|
99
|
+
### `.before(k:K)` O(log n)
|
|
100
|
+
|
|
101
|
+
Returns the key/value pair whose key is strictly less than the provided
|
|
102
|
+
key, or `undefined` if no such pair exists in the map.
|
|
103
|
+
|
|
104
|
+
### `.delete(k:K)` O(log n)
|
|
105
|
+
|
|
106
|
+
Removes the given key from the map. Returns the value that was mapped
|
|
107
|
+
to the key, or `undefined` if the key was not in the map.
|
|
108
|
+
|
|
109
|
+
### `.deleteAll(i:Iterable<K>)` O(k * log n)
|
|
110
|
+
|
|
111
|
+
Removes every key provided by the given iterable from the map.
|
|
112
|
+
Returns the number of pairs that were deleted.
|
|
113
|
+
|
|
114
|
+
### `.drop(f:(pair:Pair<K,V>)=>boolean)` O(n)
|
|
115
|
+
|
|
116
|
+
Safely removes all key/value pairs that match the provided predicate
|
|
117
|
+
function from the map. Use this instead of calling `delete` from within
|
|
118
|
+
an iteration. Returns the number of pairs that were dropped.
|
|
119
|
+
|
|
120
|
+
### `.from(k:K)` O(log n)
|
|
121
|
+
|
|
122
|
+
Returns the key/value pair whose key is greater than or equal to the provided
|
|
123
|
+
key, or `undefined` if no such pair exists in the map.
|
|
124
|
+
|
|
125
|
+
### `.get(k:K)` O(log n)
|
|
126
|
+
|
|
127
|
+
Returns the value for the given key, or `undefined` if no such key exists
|
|
128
|
+
in the map.
|
|
129
|
+
|
|
130
|
+
### `.has(k:K)` O(log n)
|
|
131
|
+
|
|
132
|
+
Returns true if the map contains the given key, or false if it doesn't.
|
|
133
|
+
|
|
134
|
+
### `.hasAll(i:Iterable<K>)` O(k * log n)
|
|
135
|
+
|
|
136
|
+
Returns true if the map contains every key produced by the given iterable,
|
|
137
|
+
or false if it doesn't.
|
|
138
|
+
|
|
139
|
+
### `.hear(f:(event:DSEvent<Pair<K,V>>))` O(1)
|
|
140
|
+
|
|
141
|
+
Adds a listener to the map. Any time the map changes (and _only_ when
|
|
142
|
+
the map actually changes), the listener function will be called.
|
|
143
|
+
|
|
144
|
+
Returns a number that can be sent to `unhear` to stop receiving events.
|
|
145
|
+
|
|
146
|
+
### `.keys()` O(1)
|
|
147
|
+
|
|
148
|
+
Returns an iterable iterator over the keys in the map.
|
|
149
|
+
|
|
150
|
+
### `.range(start:K, end:K, include?:Include)` O(1)
|
|
151
|
+
|
|
152
|
+
Returns an iterable over a range of keys in the map. The
|
|
153
|
+
iterator will produce key/value pairs.
|
|
154
|
+
|
|
155
|
+
The optional third parameter specifies whether the range is inclusive
|
|
156
|
+
or exclusive, and must be one of the following four constants:
|
|
157
|
+
|
|
158
|
+
1. `IN_EX`: Start is inclusive, end is exclusive. This is the default.
|
|
159
|
+
2. `IN_IN`: Both start and end are inclusive.
|
|
160
|
+
3. `EX_IN`: Start is exclusive, end is inclusive.
|
|
161
|
+
4. `EX_EX`: Both start and end are exclusive.
|
|
162
|
+
|
|
163
|
+
### `.rank(k:K)` O(log n)
|
|
164
|
+
|
|
165
|
+
Returns the index of the given key in the map's sort order, or
|
|
166
|
+
`undefined` if the key is not in the map.
|
|
167
|
+
|
|
168
|
+
### `.replace(pairs:Pairs<K,V>)` O(k * log n)
|
|
169
|
+
|
|
170
|
+
Replaces the content of this map with new key/value pairs.
|
|
171
|
+
|
|
172
|
+
The provided pairs can be in one of the three forms accepted by
|
|
173
|
+
`TMap`'s constructor (objects with key and value fields, tuples of
|
|
174
|
+
keys and values, or, for string keys, a JavaScript object.)
|
|
175
|
+
|
|
176
|
+
### `.reversed()` O(1)
|
|
177
|
+
|
|
178
|
+
Returns an iterable iterator over the pairs in the map, in reverse
|
|
179
|
+
sort order.
|
|
180
|
+
|
|
181
|
+
### `.set(key:K, value:V)` O(log n)
|
|
182
|
+
|
|
183
|
+
Adds a pair to the map.
|
|
184
|
+
|
|
185
|
+
If the map uses unique keys and the key already exists in the map,
|
|
186
|
+
then `set` will replace the value for that key and return the old key.
|
|
187
|
+
|
|
188
|
+
Otherwise, `set` will add a new pair to the map and return `undefined`.
|
|
189
|
+
|
|
190
|
+
### `.setAll(pairs:Pairs<K,V>)` O(k * log n)
|
|
191
|
+
|
|
192
|
+
Adds many pairs to the map. Returns the number of pairs that were newly
|
|
193
|
+
added. (The number returned does not include any keys that were
|
|
194
|
+
already in a unique map.)
|
|
195
|
+
|
|
196
|
+
The provided pairs can be in one of the three forms accepted by
|
|
197
|
+
`TMap`'s constructor (objects with key and value fields, tuples of
|
|
198
|
+
keys and values, or, for string keys, a JavaScript object.)
|
|
199
|
+
|
|
200
|
+
### `.slice(start:number, end:number)`
|
|
201
|
+
|
|
202
|
+
Returns an iterable over a range of indices. `end` is always exclusive.
|
|
203
|
+
|
|
204
|
+
### `.to(k:K)` O(log n)
|
|
205
|
+
|
|
206
|
+
Returns the key/value pair whose key is less than or equal to the provided
|
|
207
|
+
key, or `undefined` if no such pair exists in the map.
|
|
208
|
+
|
|
209
|
+
### `.unhear(n:number)` O(1)
|
|
210
|
+
|
|
211
|
+
Removes the listener registered with the specified number.
|
|
212
|
+
|
|
213
|
+
### `.values()` O(1)
|
|
214
|
+
|
|
215
|
+
Returns an iterable iterator over the values in the map.
|
|
216
|
+
|
|
217
|
+
## Operations by Kind
|
|
218
|
+
|
|
219
|
+
### Listeners
|
|
220
|
+
|
|
221
|
+
* `hear(eventHandler)` to get events when the map changes
|
|
222
|
+
* `unhear(n)` to stop listening for changes
|
|
223
|
+
|
|
224
|
+
### Iterators
|
|
225
|
+
|
|
226
|
+
* `TMap` itself is an iterable, so you can use it directly in `for...of`
|
|
227
|
+
* `i` to get an iterator
|
|
228
|
+
* `keys()` if you just want the keys
|
|
229
|
+
* `range(start,end,include)` for partial iteration over a range of keys
|
|
230
|
+
* `reversed()` to iterate backwards
|
|
231
|
+
* `slice(start,end)` for partial iteration over a range of indices
|
|
232
|
+
* `values()` if you just want the values
|
|
233
|
+
|
|
234
|
+
### Keys
|
|
235
|
+
|
|
236
|
+
* `get(key)` to get the value for a key
|
|
237
|
+
* `has(key)` to determine if the key exists
|
|
238
|
+
* `hasAll(iterable)` to determin if all the keys exist
|
|
239
|
+
* `only` to get the only pair
|
|
240
|
+
|
|
241
|
+
### Ordering
|
|
242
|
+
|
|
243
|
+
* `get compare` for the sort function
|
|
244
|
+
|
|
245
|
+
* `first` to get the first pair
|
|
246
|
+
* `last` to get the last pair
|
|
247
|
+
|
|
248
|
+
* `after(key)` to get the pair > key
|
|
249
|
+
* `before(key)` to get the pair < key
|
|
250
|
+
* `from(key)` to get the pair >= key
|
|
251
|
+
* `to(key)` to get the pair <= key
|
|
252
|
+
|
|
253
|
+
### Indices
|
|
254
|
+
|
|
255
|
+
* `at(index)` to get the key at an index
|
|
256
|
+
* `rank(key)` to get the index of a key
|
|
257
|
+
* `size` to get the number of pairs
|
|
258
|
+
|
|
259
|
+
### Adding/Changing
|
|
260
|
+
|
|
261
|
+
* `set compare` to re-sort the map
|
|
262
|
+
* `set(key,value)` add a pair to the map
|
|
263
|
+
* `setAll(pairs)` add many pairs to the map
|
|
264
|
+
* `replace(pairs)` clear the map then add many pairs
|
|
265
|
+
|
|
266
|
+
### Deleting
|
|
267
|
+
|
|
268
|
+
* `clear()` delete everything
|
|
269
|
+
* `delete(key)` delete a specific key
|
|
270
|
+
* `deleteAll(iterable)` delete many keys
|
|
271
|
+
* `drop(predicate)` delete pairs that match a predicate
|
package/dist/index.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export declare const EX_EX: {
|
|
|
21
21
|
end: boolean;
|
|
22
22
|
};
|
|
23
23
|
type N<K extends {}, V> = Node<K, V> | undefined;
|
|
24
|
-
declare class Node<K extends {}, V> {
|
|
24
|
+
declare class Node<K extends {}, V> implements Pair<K, V> {
|
|
25
25
|
readonly key: K;
|
|
26
26
|
value: V;
|
|
27
27
|
weight: number;
|
|
@@ -42,6 +42,7 @@ export interface Conf<K extends {}, V> {
|
|
|
42
42
|
readonly valueEq: (a: V, b: V) => boolean;
|
|
43
43
|
}
|
|
44
44
|
export declare class TMap<K extends {}, V> extends Base<Pair<K, V>> {
|
|
45
|
+
#private;
|
|
45
46
|
private root;
|
|
46
47
|
private _compare;
|
|
47
48
|
protected conf: Conf<K, V>;
|
|
@@ -50,23 +51,20 @@ export declare class TMap<K extends {}, V> extends Base<Pair<K, V>> {
|
|
|
50
51
|
toEmpty(): TMap<K, V>;
|
|
51
52
|
get size(): number;
|
|
52
53
|
clear(): void;
|
|
53
|
-
replace(i: Pairs<K, V>):
|
|
54
|
-
drop(f: (pair: Pair<K, V>) => boolean):
|
|
54
|
+
replace(i: Pairs<K, V>): boolean;
|
|
55
|
+
drop(f: (pair: Pair<K, V>) => boolean): number;
|
|
55
56
|
get compare(): (a: K, b: K) => number;
|
|
56
57
|
set compare(cmp: (a: K, b: K) => number);
|
|
57
|
-
get keyEq(): (a: K, b: K) => boolean;
|
|
58
|
-
get unique(): boolean;
|
|
59
58
|
at(i: number): Pair<K, V>;
|
|
60
59
|
get only(): Pair<K, V>;
|
|
61
60
|
get first(): Pair<K, V> | undefined;
|
|
62
61
|
get last(): Pair<K, V> | undefined;
|
|
63
|
-
[Symbol.iterator](): Generator<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
range(startKey: K, endKey: K, inc?: Include): Generator<Pair<K, V>, void, unknown>;
|
|
62
|
+
[Symbol.iterator](): Generator<Pair<K, V>>;
|
|
63
|
+
keys(): Iterable<K>;
|
|
64
|
+
values(): Iterable<V>;
|
|
65
|
+
reversed(): Iterable<Pair<K, V>>;
|
|
66
|
+
range(startKey: K, endKey: K, inc?: Include): Iterable<Pair<K, V>>;
|
|
67
|
+
slice(start: number, end: number): Iterable<Pair<K, V>>;
|
|
70
68
|
private find;
|
|
71
69
|
get(key: K): V | undefined;
|
|
72
70
|
has(key: K): boolean;
|
|
@@ -74,6 +72,7 @@ export declare class TMap<K extends {}, V> extends Base<Pair<K, V>> {
|
|
|
74
72
|
to(key: K): Pair<K, V> | undefined;
|
|
75
73
|
after(key: K): Pair<K, V> | undefined;
|
|
76
74
|
before(key: K): Pair<K, V> | undefined;
|
|
75
|
+
hasAll(keys: Iterable<K>): boolean;
|
|
77
76
|
rank(key: K): number | undefined;
|
|
78
77
|
protected rawPut(key: K, value: V): {
|
|
79
78
|
node: Node<K, V>;
|
|
@@ -82,7 +81,9 @@ export declare class TMap<K extends {}, V> extends Base<Pair<K, V>> {
|
|
|
82
81
|
set(key: K, value: V): V | undefined;
|
|
83
82
|
setAll(entries: Pairs<K, V>): number;
|
|
84
83
|
delete(key: K): V | undefined;
|
|
85
|
-
private
|
|
84
|
+
private delete2;
|
|
85
|
+
deleteAll(i: Iterable<K>): number;
|
|
86
|
+
private rawDelete;
|
|
86
87
|
private weigh;
|
|
87
88
|
}
|
|
88
89
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { Base } from "dubc-ds-base";
|
|
2
|
+
import { iterable } from "dubc-ds-iterables";
|
|
2
3
|
import { forEach } from "dubc-ds-pairs";
|
|
3
4
|
export const IN_IN = { start: true, end: true };
|
|
4
5
|
export const IN_EX = { start: true, end: false };
|
|
5
6
|
export const EX_IN = { start: false, end: true };
|
|
6
7
|
export const EX_EX = { start: false, end: false };
|
|
7
8
|
class Node {
|
|
9
|
+
key;
|
|
10
|
+
value;
|
|
11
|
+
weight = 1;
|
|
12
|
+
parent;
|
|
13
|
+
left;
|
|
14
|
+
right;
|
|
8
15
|
constructor(key, value) {
|
|
9
16
|
this.key = key;
|
|
10
17
|
this.value = value;
|
|
11
|
-
this.weight = 1;
|
|
12
18
|
}
|
|
13
19
|
weigh() {
|
|
14
|
-
|
|
15
|
-
this.weight = 1 + ((_b = (_a = this.left) === null || _a === void 0 ? void 0 : _a.weight) !== null && _b !== void 0 ? _b : 0) + ((_d = (_c = this.right) === null || _c === void 0 ? void 0 : _c.weight) !== null && _d !== void 0 ? _d : 0);
|
|
20
|
+
this.weight = 1 + (this.left?.weight ?? 0) + (this.right?.weight ?? 0);
|
|
16
21
|
}
|
|
17
22
|
index() {
|
|
18
23
|
let i = -1;
|
|
@@ -60,7 +65,7 @@ class Node {
|
|
|
60
65
|
rotateLeft() {
|
|
61
66
|
let a = this;
|
|
62
67
|
let b = a.right;
|
|
63
|
-
let r = b
|
|
68
|
+
let r = b?.left;
|
|
64
69
|
b.parent = a.parent;
|
|
65
70
|
b.left = a;
|
|
66
71
|
a.parent = b;
|
|
@@ -68,13 +73,13 @@ class Node {
|
|
|
68
73
|
if (r)
|
|
69
74
|
r.parent = a;
|
|
70
75
|
a.weigh();
|
|
71
|
-
b
|
|
76
|
+
b?.weigh();
|
|
72
77
|
return b;
|
|
73
78
|
}
|
|
74
79
|
rotateRight() {
|
|
75
80
|
let a = this;
|
|
76
81
|
let b = a.left;
|
|
77
|
-
let l = b
|
|
82
|
+
let l = b?.right;
|
|
78
83
|
b.parent = a.parent;
|
|
79
84
|
b.right = a;
|
|
80
85
|
a.parent = b;
|
|
@@ -82,7 +87,7 @@ class Node {
|
|
|
82
87
|
if (l)
|
|
83
88
|
l.parent = a;
|
|
84
89
|
a.weigh();
|
|
85
|
-
b
|
|
90
|
+
b?.weigh();
|
|
86
91
|
return b;
|
|
87
92
|
}
|
|
88
93
|
}
|
|
@@ -93,6 +98,9 @@ const LE = { lset: true, rset: false, eset: true, eright: true };
|
|
|
93
98
|
const GT = { lset: false, rset: true, eset: false, eright: true };
|
|
94
99
|
const GE = { lset: false, rset: true, eset: true, eright: false };
|
|
95
100
|
export class TMap extends Base {
|
|
101
|
+
root;
|
|
102
|
+
_compare;
|
|
103
|
+
conf;
|
|
96
104
|
constructor(input, c) {
|
|
97
105
|
super();
|
|
98
106
|
this._compare = c.compare;
|
|
@@ -104,15 +112,9 @@ export class TMap extends Base {
|
|
|
104
112
|
return new TMap(pairs, conf);
|
|
105
113
|
}
|
|
106
114
|
toEmpty() {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
compare: this._compare,
|
|
111
|
-
};
|
|
112
|
-
const r = new TMap(this, conf2);
|
|
113
|
-
return r;
|
|
114
|
-
}
|
|
115
|
-
get size() { var _a, _b; return (_b = (_a = this.root) === null || _a === void 0 ? void 0 : _a.weight) !== null && _b !== void 0 ? _b : 0; }
|
|
115
|
+
return new TMap([], this.conf);
|
|
116
|
+
}
|
|
117
|
+
get size() { return this.root?.weight ?? 0; }
|
|
116
118
|
clear() {
|
|
117
119
|
const sz = this.size;
|
|
118
120
|
this.root = undefined;
|
|
@@ -128,23 +130,32 @@ export class TMap extends Base {
|
|
|
128
130
|
this.fire({ cleared: sz });
|
|
129
131
|
else
|
|
130
132
|
this.fire({ cleared: sz, added: { items: this, at: 0 } });
|
|
133
|
+
return true;
|
|
131
134
|
}
|
|
132
|
-
else if (this.root !== undefined)
|
|
135
|
+
else if (this.root !== undefined) {
|
|
133
136
|
this.fire({ added: { items: this, at: 0 } });
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
134
142
|
}
|
|
135
143
|
drop(f) {
|
|
136
144
|
let n = this.first;
|
|
137
145
|
let at = 0;
|
|
146
|
+
let c = 0;
|
|
138
147
|
while (n !== undefined) {
|
|
139
148
|
const x = n;
|
|
140
149
|
n = n.next();
|
|
141
150
|
if (f(x)) {
|
|
142
|
-
this.
|
|
151
|
+
this.rawDelete(x);
|
|
143
152
|
this.fire({ deleted: { items: [x], at: at } });
|
|
153
|
+
c++;
|
|
144
154
|
}
|
|
145
155
|
else
|
|
146
156
|
at++;
|
|
147
157
|
}
|
|
158
|
+
return c;
|
|
148
159
|
}
|
|
149
160
|
get compare() { return this._compare; }
|
|
150
161
|
set compare(cmp) {
|
|
@@ -159,21 +170,19 @@ export class TMap extends Base {
|
|
|
159
170
|
}
|
|
160
171
|
this.fire({ cleared: this.size, added: { items: this, at: 0 } });
|
|
161
172
|
}
|
|
162
|
-
get keyEq() { return (a, b) => this._compare(a, b) === 0; }
|
|
163
|
-
get unique() { return this.conf.unique; }
|
|
164
173
|
at(i) {
|
|
165
|
-
var _a;
|
|
166
174
|
let node = this.root;
|
|
167
175
|
let weight = this.size;
|
|
168
|
-
|
|
176
|
+
if (!Number.isSafeInteger(i))
|
|
177
|
+
throw new TypeError("index must be an integer");
|
|
169
178
|
if (i < 0 || i >= weight)
|
|
170
179
|
throw new TypeError("bounds");
|
|
171
180
|
while (true) {
|
|
172
|
-
const left = node
|
|
173
|
-
const lw =
|
|
181
|
+
const left = node?.left;
|
|
182
|
+
const lw = left?.weight ?? 0;
|
|
174
183
|
if (i > lw) {
|
|
175
184
|
i -= lw + 1;
|
|
176
|
-
node = node
|
|
185
|
+
node = node?.right;
|
|
177
186
|
}
|
|
178
187
|
else if (i < lw) {
|
|
179
188
|
node = left;
|
|
@@ -213,28 +222,31 @@ export class TMap extends Base {
|
|
|
213
222
|
n = n.next();
|
|
214
223
|
}
|
|
215
224
|
}
|
|
216
|
-
|
|
225
|
+
*#keys() {
|
|
217
226
|
for (const x of this)
|
|
218
227
|
yield x.key;
|
|
219
228
|
}
|
|
220
|
-
|
|
221
|
-
return this
|
|
229
|
+
keys() {
|
|
230
|
+
return iterable(() => this.#keys());
|
|
222
231
|
}
|
|
223
|
-
|
|
232
|
+
*#values() {
|
|
224
233
|
for (const x of this)
|
|
225
234
|
yield x.value;
|
|
226
235
|
}
|
|
227
|
-
|
|
228
|
-
return this
|
|
236
|
+
values() {
|
|
237
|
+
return iterable(() => this.#values());
|
|
229
238
|
}
|
|
230
|
-
|
|
239
|
+
*#reversed() {
|
|
231
240
|
let n = this.last;
|
|
232
241
|
while (n) {
|
|
233
242
|
yield n;
|
|
234
243
|
n = n.prev();
|
|
235
244
|
}
|
|
236
245
|
}
|
|
237
|
-
|
|
246
|
+
reversed() {
|
|
247
|
+
return iterable(() => this.#reversed());
|
|
248
|
+
}
|
|
249
|
+
*#range(startKey, endKey, inc) {
|
|
238
250
|
const start = this.find(startKey, inc.start ? GE : GT);
|
|
239
251
|
const cmp = this.compare;
|
|
240
252
|
if (inc.end)
|
|
@@ -246,10 +258,25 @@ export class TMap extends Base {
|
|
|
246
258
|
yield n;
|
|
247
259
|
}
|
|
248
260
|
}
|
|
261
|
+
range(startKey, endKey, inc = IN_EX) {
|
|
262
|
+
return iterable(() => this.#range(startKey, endKey, inc));
|
|
263
|
+
}
|
|
264
|
+
slice(start, end) {
|
|
265
|
+
if (end <= start)
|
|
266
|
+
throw new TypeError("end <= start");
|
|
267
|
+
const s = this.at(start);
|
|
268
|
+
if (end === this.size) {
|
|
269
|
+
return this.range(s.key, this.last.key, IN_IN);
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
const e = this.at(end);
|
|
273
|
+
return this.range(s.key, e.key, IN_EX);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
249
276
|
find(key, op = EQ) {
|
|
250
277
|
let node = this.root;
|
|
251
278
|
let ret;
|
|
252
|
-
const unique = this.unique;
|
|
279
|
+
const unique = this.conf.unique;
|
|
253
280
|
const cmp = this.compare;
|
|
254
281
|
while (node) {
|
|
255
282
|
const c = cmp(node.key, key);
|
|
@@ -275,19 +302,24 @@ export class TMap extends Base {
|
|
|
275
302
|
}
|
|
276
303
|
return ret;
|
|
277
304
|
}
|
|
278
|
-
get(key) {
|
|
305
|
+
get(key) { return this.find(key, EQ)?.value; }
|
|
279
306
|
has(key) { return this.find(key, EQ) !== undefined; }
|
|
280
307
|
from(key) { return this.find(key, GE); }
|
|
281
308
|
to(key) { return this.find(key, LE); }
|
|
282
309
|
after(key) { return this.find(key, GT); }
|
|
283
310
|
before(key) { return this.find(key, LT); }
|
|
311
|
+
hasAll(keys) {
|
|
312
|
+
for (const x of keys)
|
|
313
|
+
if (!this.has(x))
|
|
314
|
+
return false;
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
284
317
|
rank(key) {
|
|
285
|
-
|
|
286
|
-
return (_a = this.find(key, EQ)) === null || _a === void 0 ? void 0 : _a.index();
|
|
318
|
+
return this.find(key, EQ)?.index();
|
|
287
319
|
}
|
|
288
320
|
rawPut(key, value) {
|
|
289
321
|
let previous = undefined, node = this.root;
|
|
290
|
-
let unique = this.unique ? 0 : undefined;
|
|
322
|
+
let unique = this.conf.unique ? 0 : undefined;
|
|
291
323
|
let c = 0, cmp = this.compare;
|
|
292
324
|
while (node) {
|
|
293
325
|
c = cmp(node.key, key);
|
|
@@ -347,12 +379,26 @@ export class TMap extends Base {
|
|
|
347
379
|
const node = this.find(key);
|
|
348
380
|
if (node === undefined)
|
|
349
381
|
return undefined;
|
|
382
|
+
return this.delete2(node);
|
|
383
|
+
}
|
|
384
|
+
delete2(node) {
|
|
350
385
|
const at = node.index();
|
|
351
|
-
this.
|
|
386
|
+
this.rawDelete(node);
|
|
352
387
|
this.fire({ deleted: { items: [node], at } });
|
|
353
388
|
return node.value;
|
|
354
389
|
}
|
|
355
|
-
|
|
390
|
+
deleteAll(i) {
|
|
391
|
+
let c = 0;
|
|
392
|
+
for (const x of i) {
|
|
393
|
+
const node = this.find(x);
|
|
394
|
+
if (node !== undefined) {
|
|
395
|
+
c++;
|
|
396
|
+
this.delete2(node);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return c;
|
|
400
|
+
}
|
|
401
|
+
rawDelete(node) {
|
|
356
402
|
const p = node.parent;
|
|
357
403
|
let left = node.left, right = node.right;
|
|
358
404
|
let bal;
|
|
@@ -404,13 +450,13 @@ export class TMap extends Base {
|
|
|
404
450
|
let lw = weight(left);
|
|
405
451
|
let rw = weight(right);
|
|
406
452
|
if (rw * 5 + 2 < lw * 2) {
|
|
407
|
-
if (weight(left
|
|
408
|
-
node.left = left
|
|
453
|
+
if (weight(left?.left) * 5 < lw * 2)
|
|
454
|
+
node.left = left?.rotateLeft();
|
|
409
455
|
node = node.rotateRight();
|
|
410
456
|
}
|
|
411
457
|
else if (lw * 5 + 2 < rw * 2) {
|
|
412
|
-
if (weight(right
|
|
413
|
-
node.right = right
|
|
458
|
+
if (weight(right?.right) * 5 < rw * 2)
|
|
459
|
+
node.right = right?.rotateRight();
|
|
414
460
|
node = node.rotateLeft();
|
|
415
461
|
}
|
|
416
462
|
else {
|
|
@@ -427,7 +473,6 @@ export class TMap extends Base {
|
|
|
427
473
|
}
|
|
428
474
|
}
|
|
429
475
|
function weight(n) {
|
|
430
|
-
|
|
431
|
-
return (_a = n === null || n === void 0 ? void 0 : n.weight) !== null && _a !== void 0 ? _a : 0;
|
|
476
|
+
return n?.weight ?? 0;
|
|
432
477
|
}
|
|
433
478
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dubc-ds-tmap",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2",
|
|
5
5
|
"description": "Observable map based on a weighted binary search tree.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -26,14 +26,15 @@
|
|
|
26
26
|
},
|
|
27
27
|
"homepage": "https://github.com/p-jack/dubc#readme",
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"
|
|
30
|
-
"dubc-ds-
|
|
29
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
30
|
+
"dubc-ds-base": "^1.0.3",
|
|
31
|
+
"dubc-ds-iterables": "^1.0.0",
|
|
32
|
+
"dubc-ds-pairs": "^1.0.2",
|
|
33
|
+
"vitest": "^4.1.8"
|
|
31
34
|
},
|
|
32
35
|
"devDependencies": {
|
|
33
|
-
"
|
|
34
|
-
"dubc-ds-test": "^1.0.1",
|
|
36
|
+
"dubc-ds-test": "^1.0.2",
|
|
35
37
|
"jsdom": "^27.0.1",
|
|
36
|
-
"typescript": "^5.9.3"
|
|
37
|
-
"vitest": "^3.2.4"
|
|
38
|
+
"typescript": "^5.9.3"
|
|
38
39
|
}
|
|
39
40
|
}
|