dubc-ds-tmap 1.0.0 → 1.0.1

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
@@ -1,3 +1,261 @@
1
1
  # dubc-ds-tmap
2
2
 
3
- Observable sorted map based on a weighted binary search tree.
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 iterator 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
+ ### `.unhear(n:number)` O(1)
201
+
202
+ Removes the listener registered with the specified number.
203
+
204
+ ### `.values()` O(1)
205
+
206
+ Returns an iterable iterator over the values in the map.
207
+
208
+ ## Operations by Kind
209
+
210
+ ### Listeners
211
+
212
+ * `hear(eventHandler)` to get events when the map changes
213
+ * `unhear(n)` to stop listening for changes
214
+
215
+ ### Iterators
216
+
217
+ * `TMap` itself is an iterable, so you can use it directly in `for...of`
218
+ * `i` to get an iterator
219
+ * `keys()` if you just want the keys
220
+ * `range(start,end,include)` for partial iteration
221
+ * `reversed()` to iterate backwards
222
+ * `values()` if you just want the values
223
+
224
+ ### Keys
225
+
226
+ * `get(key)` to get the value for a key
227
+ * `has(key)` to determine if the key exists
228
+ * `hasAll(iterable)` to determin if all the keys exist
229
+ * `only` to get the only pair
230
+
231
+ ### Ordering
232
+
233
+ * `get compare` for the sort function
234
+
235
+ * `first` to get the first pair
236
+ * `last` to get the last pair
237
+
238
+ * `after(key)` to get the pair > key
239
+ * `before(key)` to get the pair < key
240
+ * `from(key)` to get the pair >= key
241
+ * `to(key)` to get the pair <= key
242
+
243
+ ### Indices
244
+
245
+ * `at(index)` to get the key at an index
246
+ * `rank(key)` to get the index of a key
247
+ * `size` to get the number of pairs
248
+
249
+ ### Adding/Changing
250
+
251
+ * `set compare` to re-sort the map
252
+ * `set(key,value)` add a pair to the map
253
+ * `setAll(pairs)` add many pairs to the map
254
+ * `replace(pairs)` clear the map then add many pairs
255
+
256
+ ### Deleting
257
+
258
+ * `clear()` delete everything
259
+ * `delete(key)` delete a specific key
260
+ * `deleteAll(iterable)` delete many keys
261
+ * `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;
@@ -51,20 +51,16 @@ export declare class TMap<K extends {}, V> extends Base<Pair<K, V>> {
51
51
  get size(): number;
52
52
  clear(): void;
53
53
  replace(i: Pairs<K, V>): void;
54
- drop(f: (pair: Pair<K, V>) => boolean): void;
54
+ drop(f: (pair: Pair<K, V>) => boolean): number;
55
55
  get compare(): (a: K, b: K) => number;
56
56
  set compare(cmp: (a: K, b: K) => number);
57
- get keyEq(): (a: K, b: K) => boolean;
58
- get unique(): boolean;
59
57
  at(i: number): Pair<K, V>;
60
58
  get only(): Pair<K, V>;
61
59
  get first(): Pair<K, V> | undefined;
62
60
  get last(): Pair<K, V> | undefined;
63
- [Symbol.iterator](): Generator<Node<K, V>, void, unknown>;
64
- private keys2;
65
- get keys(): Iterable<K>;
66
- private values2;
67
- get values(): Iterable<V>;
61
+ [Symbol.iterator](): Generator<Pair<K, V>>;
62
+ keys(): Generator<K, void, unknown>;
63
+ values(): Generator<V, void, unknown>;
68
64
  reversed(): Generator<Pair<K, V>, void, unknown>;
69
65
  range(startKey: K, endKey: K, inc?: Include): Generator<Pair<K, V>, void, unknown>;
70
66
  private find;
@@ -74,6 +70,7 @@ export declare class TMap<K extends {}, V> extends Base<Pair<K, V>> {
74
70
  to(key: K): Pair<K, V> | undefined;
75
71
  after(key: K): Pair<K, V> | undefined;
76
72
  before(key: K): Pair<K, V> | undefined;
73
+ hasAll(keys: Iterable<K>): boolean;
77
74
  rank(key: K): number | undefined;
78
75
  protected rawPut(key: K, value: V): {
79
76
  node: Node<K, V>;
@@ -82,7 +79,9 @@ export declare class TMap<K extends {}, V> extends Base<Pair<K, V>> {
82
79
  set(key: K, value: V): V | undefined;
83
80
  setAll(entries: Pairs<K, V>): number;
84
81
  delete(key: K): V | undefined;
85
- private remove;
82
+ private delete2;
83
+ deleteAll(i: Iterable<K>): number;
84
+ private rawDelete;
86
85
  private weigh;
87
86
  }
88
87
  export {};
package/dist/index.js CHANGED
@@ -104,13 +104,7 @@ export class TMap extends Base {
104
104
  return new TMap(pairs, conf);
105
105
  }
106
106
  toEmpty() {
107
- const conf2 = {
108
- unique: this.conf.unique,
109
- valueEq: this.conf.valueEq,
110
- compare: this._compare,
111
- };
112
- const r = new TMap(this, conf2);
113
- return r;
107
+ return new TMap([], this.conf);
114
108
  }
115
109
  get size() { var _a, _b; return (_b = (_a = this.root) === null || _a === void 0 ? void 0 : _a.weight) !== null && _b !== void 0 ? _b : 0; }
116
110
  clear() {
@@ -135,16 +129,19 @@ export class TMap extends Base {
135
129
  drop(f) {
136
130
  let n = this.first;
137
131
  let at = 0;
132
+ let c = 0;
138
133
  while (n !== undefined) {
139
134
  const x = n;
140
135
  n = n.next();
141
136
  if (f(x)) {
142
- this.remove(x);
137
+ this.rawDelete(x);
143
138
  this.fire({ deleted: { items: [x], at: at } });
139
+ c++;
144
140
  }
145
141
  else
146
142
  at++;
147
143
  }
144
+ return c;
148
145
  }
149
146
  get compare() { return this._compare; }
150
147
  set compare(cmp) {
@@ -159,13 +156,12 @@ export class TMap extends Base {
159
156
  }
160
157
  this.fire({ cleared: this.size, added: { items: this, at: 0 } });
161
158
  }
162
- get keyEq() { return (a, b) => this._compare(a, b) === 0; }
163
- get unique() { return this.conf.unique; }
164
159
  at(i) {
165
160
  var _a;
166
161
  let node = this.root;
167
162
  let weight = this.size;
168
- // if (i < 0) i += weight // TODO
163
+ if (!Number.isSafeInteger(i))
164
+ throw new TypeError("index must be an integer");
169
165
  if (i < 0 || i >= weight)
170
166
  throw new TypeError("bounds");
171
167
  while (true) {
@@ -213,20 +209,14 @@ export class TMap extends Base {
213
209
  n = n.next();
214
210
  }
215
211
  }
216
- *keys2() {
212
+ *keys() {
217
213
  for (const x of this)
218
214
  yield x.key;
219
215
  }
220
- get keys() {
221
- return this.keys2();
222
- }
223
- *values2() {
216
+ *values() {
224
217
  for (const x of this)
225
218
  yield x.value;
226
219
  }
227
- get values() {
228
- return this.values2();
229
- }
230
220
  *reversed() {
231
221
  let n = this.last;
232
222
  while (n) {
@@ -249,7 +239,7 @@ export class TMap extends Base {
249
239
  find(key, op = EQ) {
250
240
  let node = this.root;
251
241
  let ret;
252
- const unique = this.unique;
242
+ const unique = this.conf.unique;
253
243
  const cmp = this.compare;
254
244
  while (node) {
255
245
  const c = cmp(node.key, key);
@@ -281,13 +271,19 @@ export class TMap extends Base {
281
271
  to(key) { return this.find(key, LE); }
282
272
  after(key) { return this.find(key, GT); }
283
273
  before(key) { return this.find(key, LT); }
274
+ hasAll(keys) {
275
+ for (const x of keys)
276
+ if (!this.has(x))
277
+ return false;
278
+ return true;
279
+ }
284
280
  rank(key) {
285
281
  var _a;
286
282
  return (_a = this.find(key, EQ)) === null || _a === void 0 ? void 0 : _a.index();
287
283
  }
288
284
  rawPut(key, value) {
289
285
  let previous = undefined, node = this.root;
290
- let unique = this.unique ? 0 : undefined;
286
+ let unique = this.conf.unique ? 0 : undefined;
291
287
  let c = 0, cmp = this.compare;
292
288
  while (node) {
293
289
  c = cmp(node.key, key);
@@ -347,12 +343,26 @@ export class TMap extends Base {
347
343
  const node = this.find(key);
348
344
  if (node === undefined)
349
345
  return undefined;
346
+ return this.delete2(node);
347
+ }
348
+ delete2(node) {
350
349
  const at = node.index();
351
- this.remove(node);
350
+ this.rawDelete(node);
352
351
  this.fire({ deleted: { items: [node], at } });
353
352
  return node.value;
354
353
  }
355
- remove(node) {
354
+ deleteAll(i) {
355
+ let c = 0;
356
+ for (const x of i) {
357
+ const node = this.find(x);
358
+ if (node !== undefined) {
359
+ c++;
360
+ this.delete2(node);
361
+ }
362
+ }
363
+ return c;
364
+ }
365
+ rawDelete(node) {
356
366
  const p = node.parent;
357
367
  let left = node.left, right = node.right;
358
368
  let bal;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dubc-ds-tmap",
3
3
  "type": "module",
4
- "version": "1.0.0",
4
+ "version": "1.0.1",
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",
@@ -27,7 +27,7 @@
27
27
  "homepage": "https://github.com/p-jack/dubc#readme",
28
28
  "dependencies": {
29
29
  "dubc-ds-base": "^1.0.2",
30
- "dubc-ds-pairs": "^1.0.0"
30
+ "dubc-ds-pairs": "^1.0.1"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@vitest/coverage-v8": "^3.2.4",