atomic-fns 1.1.7 → 1.2.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.
Files changed (46) hide show
  1. package/README.md +51 -12
  2. package/dist/collections/BTree.d.ts +11 -10
  3. package/dist/collections/BTree.js +58 -80
  4. package/dist/collections/Heap.js +1 -1
  5. package/dist/collections/LRUCache.d.ts +6 -3
  6. package/dist/collections/LRUCache.js +24 -11
  7. package/dist/collections/SortedMap.js +2 -2
  8. package/dist/collections/SortedTree.d.ts +2 -2
  9. package/dist/collections/SortedTree.js +3 -2
  10. package/dist/collections/SplayTree.d.ts +7 -5
  11. package/dist/collections/SplayTree.js +39 -26
  12. package/dist/collections/Trie.d.ts +1 -1
  13. package/dist/collections/Trie.js +26 -26
  14. package/dist/collections/deque.d.ts +1 -1
  15. package/dist/collections/deque.js +17 -10
  16. package/dist/collections/frozenset.js +4 -1
  17. package/dist/collections/index.d.ts +13 -32
  18. package/dist/collections/index.js +210 -82
  19. package/dist/decimal/index.d.ts +5 -2
  20. package/dist/decimal/index.js +63 -27
  21. package/dist/functools/index.d.ts +6 -30
  22. package/dist/functools/index.js +24 -56
  23. package/dist/globals/index.d.ts +3 -20
  24. package/dist/globals/index.js +23 -39
  25. package/dist/i18n/currency.d.ts +3 -0
  26. package/dist/i18n/currency.js +22 -7
  27. package/dist/i18n/date/index.d.ts +17 -10
  28. package/dist/i18n/date/index.js +79 -58
  29. package/dist/i18n/date/utils.d.ts +4 -5
  30. package/dist/i18n/date/utils.js +15 -113
  31. package/dist/i18n/duration.d.ts +11 -10
  32. package/dist/i18n/duration.js +38 -32
  33. package/dist/i18n/format.d.ts +5 -2
  34. package/dist/i18n/format.js +81 -209
  35. package/dist/i18n/index.d.ts +2 -1
  36. package/dist/itertools/index.d.ts +9 -13
  37. package/dist/itertools/index.js +38 -41
  38. package/dist/math/index.d.ts +10 -27
  39. package/dist/math/index.js +16 -33
  40. package/dist/operators/index.d.ts +1 -5
  41. package/dist/operators/index.js +60 -40
  42. package/dist/random/index.js +9 -6
  43. package/dist/string/index.d.ts +1 -1
  44. package/dist/string/index.js +2 -4
  45. package/dist/types.d.ts +3 -3
  46. package/package.json +16 -21
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # atomic-fns
2
2
 
3
- [Read the Docs](https://atomic-stack.github.io/atomic-fns/)
3
+ [Read the Docs](https://jsr.io/@atomic-stack/atomic-fns/doc)
4
4
 
5
5
  ## Like Lodash, but in TypeScript and for ESNext.
6
6
 
@@ -17,7 +17,7 @@ You may prefer this library because:
17
17
 
18
18
  ## Docs
19
19
 
20
- 📖 Available at [atomic-fns](https://atomic-stack.github.io/atomic-fns/)
20
+ 📖 Available at [atomic-fns](https://jsr.io/@atomic-stack/atomic-fns/doc)
21
21
 
22
22
  ## Get Started
23
23
 
@@ -29,6 +29,12 @@ To install the package you need to have [npm](https://www.npmjs.com/get-npm) (or
29
29
  npm install atomic-fns
30
30
  ```
31
31
 
32
+ It is also on [JSR](https://jsr.io/@atomic-stack/atomic-fns), for Deno and other runtimes that read TypeScript directly:
33
+
34
+ ```bash
35
+ npx jsr add @atomic-stack/atomic-fns
36
+ ```
37
+
32
38
  ## How to use
33
39
 
34
40
  Just import any function or class you need.
@@ -36,7 +42,7 @@ Just import any function or class you need.
36
42
  ```js
37
43
  import { sorted, times, uniqueId } from 'atomic-fns'
38
44
 
39
- times(5, (i) => i)
45
+ [...times(5, (i) => i)]
40
46
  // -> [0, 1, 2, 3, 4]
41
47
 
42
48
  sorted([4, 3, 2, 1])
@@ -47,7 +53,7 @@ uniqueId('user_')
47
53
  ...
48
54
  ```
49
55
 
50
- See the full list of [modules](https://atomic-stack.github.io/atomic-fns/modules.html).
56
+ See the full list of [modules](https://jsr.io/@atomic-stack/atomic-fns/doc).
51
57
 
52
58
  ## Data model
53
59
  This library features a data model similar to [Python](https://docs.python.org/3/reference/datamodel.html#special-method-names) but not always. Only the missing parts have been implemented, while respecting the JS standards.
@@ -67,7 +73,7 @@ When working with objects, functions in this library use these custom operators
67
73
 
68
74
  ## New Types
69
75
 
70
- [`Decimal`](https://atomic-stack.github.io/atomic-fns/classes/decimal.Decimal.html) provides support for correct rounded floating point arithmetic, with arbitrary precision.
76
+ [`Decimal`](https://jsr.io/@atomic-stack/atomic-fns/doc/decimal/~/Decimal) provides support for correct rounded floating point arithmetic, with arbitrary precision.
71
77
 
72
78
  This fixes precision issues with float arithmetic, for example:
73
79
  ```
@@ -81,15 +87,48 @@ decimal('0.1').add('0.1').add('0.1').toNumber()
81
87
  Some common base interfaces and typing patterns you may have seen like `Optional`, `Result`, `Comparable`, `Iterator`, and others.
82
88
 
83
89
  ### Sorted Containers
84
- - [SortedSet](https://atomic-stack.github.io/atomic-fns/classes/collections.SortedSet.html)
85
- - [SortedMap](https://atomic-stack.github.io/atomic-fns/classes/collections.SortedMap.html)
86
- - [SortedTree](https://atomic-stack.github.io/atomic-fns/classes/collections.SortedTree.html)
90
+ - [SortedSet](https://jsr.io/@atomic-stack/atomic-fns/doc/collections/~/SortedSet)
91
+ - [SortedMap](https://jsr.io/@atomic-stack/atomic-fns/doc/collections/~/SortedMap)
92
+ - [SortedTree](https://jsr.io/@atomic-stack/atomic-fns/doc/collections/~/SortedTree)
87
93
 
88
94
  ### Other Containers
89
- - [Deque](https://atomic-stack.github.io/atomic-fns/classes/collections.Deque.html)
90
- - [FrozenSet](https://atomic-stack.github.io/atomic-fns/classes/collections.FrozenSet.html)
91
- - [LRUCache](https://atomic-stack.github.io/atomic-fns/classes/collections.LRUCache.html)
92
-
95
+ - [Deque](https://jsr.io/@atomic-stack/atomic-fns/doc/collections/~/Deque)
96
+ - [FrozenSet](https://jsr.io/@atomic-stack/atomic-fns/doc/collections/~/FrozenSet)
97
+ - [LRUCache](https://jsr.io/@atomic-stack/atomic-fns/doc/collections/~/LRUCache)
98
+
99
+
100
+ ## Upgrading to 1.2
101
+
102
+ 1.2 removes the exports that only repeated a native function, along with the deprecated `id` and the experimental `cache` decorator.
103
+
104
+ | Removed | Use instead |
105
+ | ------------------------------- | -------------------------------------- |
106
+ | `isArray` | `Array.isArray` |
107
+ | `isInteger` | `Number.isInteger` |
108
+ | `keys`, `values` | `Object.keys`, `Object.values` |
109
+ | `bind` | `Function.prototype.bind` |
110
+ | `callAsync` | `new Promise` |
111
+ | `floor`, `ceil`, `log2` | `Math.floor`, `Math.ceil`, `Math.log2` |
112
+ | `cloneArray`, `cloneTypedArray` | `clone`, `structuredClone`, `slice` |
113
+ | `IterableIterator` | `Iterator.from` |
114
+ | `id` | `identity` |
115
+ | `cache` | `memoize` |
116
+
117
+ The rest of 1.2 fixes bugs. These fixes change results that code may have worked around:
118
+
119
+ - `eq`, `lt`, `lte`, `gt` and `gte` now ask an object's own method first and honor a `false` answer, so comparing two `Decimal` values gives the right answer. `eq(0, -0)` is `true`.
120
+ - `deepEqual` and `shallowEqual` tell kinds apart, and compare Dates, RegExps and typed arrays by value.
121
+ - `merge` copies plain objects from its sources and skips `__proto__`, so it no longer changes a source or `Object.prototype`.
122
+ - `difference` removes the values of the other arrays, where it returned a symmetric difference.
123
+ - `uniq` with an iteratee returns the original elements, not the computed keys, and `sortedUniq` sorts them by that key.
124
+ - `memoize` caches an `undefined` result.
125
+ - `divmod` follows Python, so the quotient and remainder add back up to the input.
126
+ - `bisect` and `insort` find the right position in every case.
127
+ - `Deque.set` throws `KeyError` past the end, where the value was lost.
128
+ - A frozen `BTree` refuses `remove`, as it does `set`.
129
+ - `IntlDate` adds months with a clamp to the last day of the month, keeps the wall clock across DST for calendar units, and `diff` counts calendar months, quarters and years. `exact` keeps the averaged result.
130
+ - `addDuration` and `subDuration` read a date-only string as local time, as `IntlDate` does.
131
+ - `format('Z')` prints the numeric offset, such as `-05:00`.
93
132
 
94
133
  ## Contributing
95
134
  If you want to contribute to the project and make it better, your help is very welcome. Contributing is also a great way to learn more about social coding on Github, new technologies and and their ecosystems and how to make constructive, helpful bug reports, feature requests and the noblest of all contributions: a good, clean pull request. You will be listed as a **Champion** on the official site as well.
@@ -1,6 +1,6 @@
1
- import { Comparer } from '../operators/index.js';
1
+ import { type Comparer } from '../operators/index.js';
2
2
  import { Mapping } from './abc.js';
3
- import { Iteratee } from '../types';
3
+ import type { Iteratee } from '../types.js';
4
4
  export declare class BTree<K = any, V = any> extends Mapping<K, V> {
5
5
  private root;
6
6
  count: number;
@@ -14,8 +14,7 @@ export declare class BTree<K = any, V = any> extends Mapping<K, V> {
14
14
  * Initializes an empty B+ tree.
15
15
  * @param {?Iterable<[K, V]>} [entries] The initial key value pairs.
16
16
  * @param compareFn Custom function to compare pairs of elements in the tree.
17
- * If not specified, defaultComparator will be used which is valid as long as K extends DefaultComparable.
18
- * @param entries A set of key-value pairs to initialize the tree
17
+ * If not specified, {@link operators.compare} is used.
19
18
  * @param {number} [maxNodeSize=64] Branching factor (maximum items or children per node)
20
19
  * Must be in range 4..256. If undefined or <4 then default is used; if >256 then 256.
21
20
  */
@@ -64,6 +63,7 @@ export declare class BTree<K = any, V = any> extends Mapping<K, V> {
64
63
  * starts at the next higher key (according to the comparator).
65
64
  * @param reusedArray Optional array used repeatedly to store key-value
66
65
  * pairs, to avoid creating a new array on every iteration.
66
+ * Note: changing the tree during iteration may skip or repeat keys.
67
67
  */
68
68
  entries(lowestKey?: K, reusedArray?: Array<K | V>): IterableIterator<[K, V]>;
69
69
  /** Returns an iterator that provides items in reversed order.
@@ -74,6 +74,7 @@ export declare class BTree<K = any, V = any> extends Mapping<K, V> {
74
74
  * pairs, to avoid creating a new array on every iteration.
75
75
  * @param skipHighest Iff this flag is true and the highestKey exists in the
76
76
  * collection, the pair matching highestKey is skipped, not iterated.
77
+ * Note: changing the tree during iteration may skip or repeat keys.
77
78
  */
78
79
  reversed(highestKey?: K, reusedArray?: Array<K | V>, skipHighest?: boolean): IterableIterator<[K, V]>;
79
80
  private findPath;
@@ -128,8 +129,8 @@ export declare class BTree<K = any, V = any> extends Mapping<K, V> {
128
129
  * */
129
130
  floor(key: K, reusedArray?: [K, V]): [K, V] | undefined;
130
131
  /** Returns the key-value pair associated with the supplied key if it exists
131
- * or the pair associated with the next lower pair otherwise. If there is no
132
- * next lower pair, undefined is returned.
132
+ * or the next higher pair otherwise. If there is no next higher pair,
133
+ * undefined is returned.
133
134
  * @param key The key to search for.
134
135
  * @param reusedArray Optional array used repeatedly to store key-value pairs, to
135
136
  * avoid creating a new array each time you call this method.
@@ -142,8 +143,9 @@ export declare class BTree<K = any, V = any> extends Mapping<K, V> {
142
143
  * @param high This method returns when a key larger than this is reached.
143
144
  * @param includeHigh If the `high` key is present, its pair will be included
144
145
  * in the output if and only if this parameter is true. Note: if the
145
- * `low` key is present, it is always included in the output.
146
- * @param maxLength Length limit. getRange will stop scanning the tree when
146
+ * `low` key is present, it is included in the output unless `low`
147
+ * equals `high` and this parameter is false.
148
+ * @param maxLength Length limit. keysRange will stop scanning the tree when
147
149
  * the array reaches this size.
148
150
  * @description Computational complexity: O(result.length + log size)
149
151
  */
@@ -191,7 +193,7 @@ export declare class BTree<K = any, V = any> extends Mapping<K, V> {
191
193
  * function can return `{value:v}` to change the value associated
192
194
  * with the current key, `{delete:true}` to delete the current pair,
193
195
  * `{done:R}` to stop early with result R, or it can return nothing
194
- * (undefined or {}) to cause no effect and continue iterating.
196
+ * (undefined, null or {}) to cause no effect and continue iterating.
195
197
  * `{done:R}` can be combined with one of the other two commands.
196
198
  * The third argument `counter` is the number of items iterated
197
199
  * previously; it equals 0 when `onFound` is called the first time.
@@ -219,7 +221,6 @@ export declare class BTree<K = any, V = any> extends Mapping<K, V> {
219
221
  * BTree object and its leaf nodes (zero if there are no internal nodes). */
220
222
  get height(): number;
221
223
  /** Makes the object read-only to ensure it is not accidentally modified.
222
- * Freezing does not have to be permanent; unfreeze() reverses the effect.
223
224
  * This is accomplished by replacing mutator functions with a function
224
225
  * that throws an Error. Compared to using a property (e.g. this.isFrozen)
225
226
  * this implementation gives better performance in non-frozen BTrees.
@@ -43,7 +43,7 @@ class BNode {
43
43
  /// ////////////////////////////////////////////////////////////////////////
44
44
  // Shared methods /////////////////////////////////////////////////////////
45
45
  maxKey() {
46
- return this.keys[this.keys.length - 1];
46
+ return this.keys.at(-1);
47
47
  }
48
48
  // If key not found, returns i^failXor where i is the insertion index.
49
49
  // Callers that don't care whether there was a match will set failXor=0.
@@ -62,11 +62,9 @@ class BNode {
62
62
  return mid;
63
63
  else {
64
64
  // c is NaN or otherwise invalid
65
- // eslint-disable-next-line
66
65
  if (key === key)
67
66
  // at least the search key is not NaN
68
- return keys.length;
69
- // eslint-disable-next-line
67
+ return keys.length ^ failXor;
70
68
  else
71
69
  throw new KeyError(`Btree found an invalid key: ${key}`);
72
70
  }
@@ -152,7 +150,6 @@ class BNode {
152
150
  else {
153
151
  // This leaf node is full and must split
154
152
  const newRightSibling = this.splitOffRightSide();
155
- // eslint-disable-next-line
156
153
  let target = this;
157
154
  if (i > this.keys.length) {
158
155
  i -= this.keys.length;
@@ -205,10 +202,8 @@ class BNode {
205
202
  }
206
203
  else {
207
204
  v = this.reifyValues();
208
- // eslint-disable-next-line
209
205
  v.push(rhs.values.shift());
210
206
  }
211
- // eslint-disable-next-line
212
207
  this.keys.push(rhs.keys.shift());
213
208
  }
214
209
  takeFromLeft(lhs) {
@@ -222,10 +217,8 @@ class BNode {
222
217
  }
223
218
  else {
224
219
  v = this.reifyValues();
225
- // eslint-disable-next-line
226
220
  v.unshift(lhs.values.pop());
227
221
  }
228
- // eslint-disable-next-line
229
222
  this.keys.unshift(lhs.keys.pop());
230
223
  }
231
224
  splitOffRightSide() {
@@ -256,14 +249,13 @@ class BNode {
256
249
  iHigh++;
257
250
  }
258
251
  const keys = this.keys;
259
- const values = this.values;
260
252
  if (onFound !== undefined) {
261
253
  for (let i = iLow; i < iHigh; i++) {
262
254
  const key = keys[i];
263
- const result = onFound(key, values[i], count++);
264
- if (result !== undefined) {
255
+ const result = onFound(key, this.values[i], count++);
256
+ if (result != null) {
265
257
  if (editMode) {
266
- if (key !== keys[i] || this.isShared)
258
+ if (!Object.is(key, keys[i]) || this.isShared)
267
259
  throw new Error('BTree illegally changed or cloned in editRange');
268
260
  if (result.delete) {
269
261
  this.keys.splice(i, 1);
@@ -273,8 +265,8 @@ class BNode {
273
265
  i--;
274
266
  iHigh--;
275
267
  }
276
- else if (result.value) {
277
- values[i] = result.value;
268
+ else if ('value' in Object(result)) {
269
+ this.reifyValues()[i] = result.value;
278
270
  }
279
271
  }
280
272
  if (result.done !== undefined)
@@ -283,7 +275,7 @@ class BNode {
283
275
  }
284
276
  }
285
277
  else
286
- count += iHigh - iLow;
278
+ count += Math.max(iHigh - iLow, 0);
287
279
  return count;
288
280
  }
289
281
  /** Adds entire contents of right-hand sibling (rhs is left unchanged) */
@@ -292,7 +284,7 @@ class BNode {
292
284
  if (this.values === undefVals) {
293
285
  if (rhs.values === undefVals)
294
286
  return;
295
- this.values = this.values.slice(0, this.keys.length);
287
+ this.values = this.values.slice(0, this.keys.length - rhs.keys.length);
296
288
  }
297
289
  this.values.push.apply(this.values, rhs.reifyValues());
298
290
  }
@@ -318,8 +310,7 @@ export class BTree extends Mapping {
318
310
  * Initializes an empty B+ tree.
319
311
  * @param {?Iterable<[K, V]>} [entries] The initial key value pairs.
320
312
  * @param compareFn Custom function to compare pairs of elements in the tree.
321
- * If not specified, defaultComparator will be used which is valid as long as K extends DefaultComparable.
322
- * @param entries A set of key-value pairs to initialize the tree
313
+ * If not specified, {@link operators.compare} is used.
323
314
  * @param {number} [maxNodeSize=64] Branching factor (maximum items or children per node)
324
315
  * Must be in range 4..256. If undefined or <4 then default is used; if >256 then 256.
325
316
  */
@@ -365,7 +356,7 @@ export class BTree extends Mapping {
365
356
  // private forEachPair<R = number>(callback: Iteratee<K, V>, initialCounter?: number): R | number {
366
357
  // const low = this.minKey()
367
358
  // const high = this.maxKey()
368
- // // eslint-disable-next-line
359
+ //
369
360
  // return this.rangeForEach(low!, high, true, callback, initialCounter)
370
361
  // }
371
362
  /**
@@ -399,7 +390,7 @@ export class BTree extends Mapping {
399
390
  return true;
400
391
  }
401
392
  add(key) {
402
- return this.set(key, null);
393
+ return this.set(key, this.get(key, null));
403
394
  }
404
395
  /**
405
396
  * Returns true if the key exists in the B+ tree, false if not.
@@ -407,7 +398,7 @@ export class BTree extends Mapping {
407
398
  * @description Computational complexity: O(log size)
408
399
  */
409
400
  contains(key) {
410
- return this.get(key) !== undefined;
401
+ return this.rangeForEach(key, key, true) !== 0;
411
402
  }
412
403
  /**
413
404
  * Removes a single key-value pair from the B+ tree.
@@ -425,6 +416,7 @@ export class BTree extends Mapping {
425
416
  * starts at the next higher key (according to the comparator).
426
417
  * @param reusedArray Optional array used repeatedly to store key-value
427
418
  * pairs, to avoid creating a new array on every iteration.
419
+ * Note: changing the tree during iteration may skip or repeat keys.
428
420
  */
429
421
  entries(lowestKey, reusedArray) {
430
422
  const info = this.findPath(lowestKey);
@@ -434,7 +426,6 @@ export class BTree extends Mapping {
434
426
  let state = reusedArray !== undefined ? 1 : 0;
435
427
  let i = lowestKey === undefined ? -1 : leaf.indexOf(lowestKey, 0, this._compare) - 1;
436
428
  return iterator(() => {
437
- // eslint-disable-next-line
438
429
  jump: while (true) {
439
430
  switch (state) {
440
431
  case 0:
@@ -444,32 +435,29 @@ export class BTree extends Mapping {
444
435
  continue;
445
436
  case 1:
446
437
  if (++i < leaf.keys.length) {
447
- // eslint-disable-next-line
448
438
  reusedArray[0] = leaf.keys[i];
449
- // eslint-disable-next-line
450
439
  reusedArray[1] = leaf.values[i];
451
440
  return { done: false, value: reusedArray };
452
441
  }
453
442
  state = 2;
454
- // eslint-disable-next-line
455
443
  case 2:
456
444
  // Advance to the next leaf node
457
- // eslint-disable-next-line
458
445
  let level = -1;
459
446
  while (true) {
460
447
  if (++level >= nodequeue.length) {
461
448
  state = 3;
462
- // eslint-disable-next-line
463
449
  continue jump;
464
450
  }
465
451
  if (++nodeindex[level] < nodequeue[level].length)
466
452
  break;
467
453
  }
454
+ // Keys removed during iteration can leave this path empty.
468
455
  for (; level > 0; level--) {
469
- nodequeue[level - 1] = nodequeue[level][nodeindex[level]].children;
456
+ const parent = nodequeue[level][nodeindex[level]];
457
+ nodequeue[level - 1] = parent?.children ?? EmptyArray;
470
458
  nodeindex[level - 1] = 0;
471
459
  }
472
- leaf = nodequeue[0][nodeindex[0]];
460
+ leaf = nodequeue[0][nodeindex[0]] ?? EmptyLeaf;
473
461
  i = -1;
474
462
  state = reusedArray !== undefined ? 1 : 0;
475
463
  continue;
@@ -487,58 +475,53 @@ export class BTree extends Mapping {
487
475
  * pairs, to avoid creating a new array on every iteration.
488
476
  * @param skipHighest Iff this flag is true and the highestKey exists in the
489
477
  * collection, the pair matching highestKey is skipped, not iterated.
478
+ * Note: changing the tree during iteration may skip or repeat keys.
490
479
  */
491
480
  reversed(highestKey, reusedArray, skipHighest) {
492
- if (highestKey === undefined) {
493
- highestKey = this.maxKey();
494
- skipHighest = undefined;
495
- if (highestKey === undefined)
496
- return iterator(); // collection is empty
497
- }
498
- // eslint-disable-next-line
499
- let { nodequeue, nodeindex, leaf } = this.findPath(highestKey) || this.findPath(this.maxKey());
481
+ let { nodequeue, nodeindex, leaf } = this.findPath(highestKey, true) || this.findPath(undefined, true);
500
482
  assert(!nodequeue[0] || leaf === nodequeue[0][nodeindex[0]], 'wat!');
501
- let i = leaf.indexOf(highestKey, 0, this._compare);
502
- if (!skipHighest && i < leaf.keys.length && this._compare(leaf.keys[i], highestKey) <= 0)
503
- i++;
483
+ let i = leaf.keys.length;
484
+ if (highestKey !== undefined) {
485
+ i = leaf.indexOf(highestKey, 0, this._compare);
486
+ if (!skipHighest && i < leaf.keys.length && this._compare(leaf.keys[i], highestKey) <= 0)
487
+ i++;
488
+ }
504
489
  let state = reusedArray !== undefined ? 1 : 0;
505
490
  return iterator(() => {
506
- // eslint-disable-next-line
507
491
  jump: while (true) {
508
492
  switch (state) {
509
493
  case 0:
510
- if (--i >= 0)
494
+ i = Math.min(i, leaf.keys.length) - 1;
495
+ if (i >= 0)
511
496
  return { done: false, value: [leaf.keys[i], leaf.values[i]] };
512
497
  state = 2;
513
498
  continue;
514
499
  case 1:
515
- if (--i >= 0) {
516
- // eslint-disable-next-line
500
+ i = Math.min(i, leaf.keys.length) - 1;
501
+ if (i >= 0) {
517
502
  reusedArray[0] = leaf.keys[i];
518
- // eslint-disable-next-line
519
503
  reusedArray[1] = leaf.values[i];
520
504
  return { done: false, value: reusedArray };
521
505
  }
522
506
  state = 2;
523
- // eslint-disable-next-line
524
507
  case 2:
525
508
  // Advance to the next leaf node
526
- // eslint-disable-next-line
527
509
  let level = -1;
528
510
  while (true) {
529
511
  if (++level >= nodequeue.length) {
530
512
  state = 3;
531
- // eslint-disable-next-line
532
513
  continue jump;
533
514
  }
534
515
  if (--nodeindex[level] >= 0)
535
516
  break;
536
517
  }
518
+ // Keys removed during iteration can leave this path empty.
537
519
  for (; level > 0; level--) {
538
- nodequeue[level - 1] = nodequeue[level][nodeindex[level]].children;
520
+ const parent = nodequeue[level][nodeindex[level]];
521
+ nodequeue[level - 1] = parent?.children ?? EmptyArray;
539
522
  nodeindex[level - 1] = nodequeue[level - 1].length - 1;
540
523
  }
541
- leaf = nodequeue[0][nodeindex[0]];
524
+ leaf = nodequeue[0][nodeindex[0]] ?? EmptyLeaf;
542
525
  i = leaf.keys.length;
543
526
  state = reusedArray !== undefined ? 1 : 0;
544
527
  continue;
@@ -548,7 +531,7 @@ export class BTree extends Mapping {
548
531
  }
549
532
  });
550
533
  }
551
- /* Used by entries() and entriesReversed() to prepare to start iterating.
534
+ /* Used by entries() and reversed() to prepare to start iterating.
552
535
  * It develops a "node queue" for each non-leaf level of the tree.
553
536
  * Levels are numbered "bottom-up" so that level 0 is a list of leaf
554
537
  * nodes from a low-level non-leaf node. The queue at a given level L
@@ -558,7 +541,7 @@ export class BTree extends Mapping {
558
541
  * (However inside this function the order is reversed.)
559
542
  * @private
560
543
  */
561
- findPath(key) {
544
+ findPath(key, fromEnd = false) {
562
545
  let nextnode = this.root;
563
546
  let nodequeue, nodeindex;
564
547
  if (nextnode.isLeaf) {
@@ -571,7 +554,10 @@ export class BTree extends Mapping {
571
554
  nodeindex = [];
572
555
  for (let d = 0; !nextnode.isLeaf; d++) {
573
556
  nodequeue[d] = nextnode.children;
574
- nodeindex[d] = key === undefined ? 0 : nextnode.indexOf(key, 0, this._compare);
557
+ if (key !== undefined)
558
+ nodeindex[d] = nextnode.indexOf(key, 0, this._compare);
559
+ else
560
+ nodeindex[d] = fromEnd ? nodequeue[d].length - 1 : 0;
575
561
  if (nodeindex[d] >= nodequeue[d].length)
576
562
  return; // first key > maxKey()
577
563
  nextnode = nodequeue[d][nodeindex[d]];
@@ -642,12 +628,7 @@ export class BTree extends Mapping {
642
628
  }
643
629
  /** Gets an array filled with the contents of the tree, sorted by key */
644
630
  toArray() {
645
- const min = this.minKey();
646
- const max = this.maxKey();
647
- const maxLength = 2147483647;
648
- if (min !== undefined)
649
- return this.keysRange(min, max, true, maxLength);
650
- return [];
631
+ return [...this.entries()];
651
632
  }
652
633
  /** Gets a string representing the tree's data based on toArray(). */
653
634
  toString() {
@@ -660,7 +641,7 @@ export class BTree extends Mapping {
660
641
  * avoid creating a new array on every iteration.
661
642
  */
662
643
  lowerBound(key, reusedArray) {
663
- reusedArray = reusedArray || [];
644
+ reusedArray ||= [];
664
645
  if (key === undefined) {
665
646
  return this.root.minPair(reusedArray);
666
647
  }
@@ -680,7 +661,7 @@ export class BTree extends Mapping {
680
661
  * avoid creating a new array each time you call this method.
681
662
  */
682
663
  upperBound(key, reusedArray) {
683
- reusedArray = reusedArray || [];
664
+ reusedArray ||= [];
684
665
  if (key === undefined) {
685
666
  return this.root.maxPair(reusedArray);
686
667
  }
@@ -704,8 +685,8 @@ export class BTree extends Mapping {
704
685
  return this.root.getPairOrNextLower(key, this._compare, true, reusedArray || []);
705
686
  }
706
687
  /** Returns the key-value pair associated with the supplied key if it exists
707
- * or the pair associated with the next lower pair otherwise. If there is no
708
- * next lower pair, undefined is returned.
688
+ * or the next higher pair otherwise. If there is no next higher pair,
689
+ * undefined is returned.
709
690
  * @param key The key to search for.
710
691
  * @param reusedArray Optional array used repeatedly to store key-value pairs, to
711
692
  * avoid creating a new array each time you call this method.
@@ -720,16 +701,18 @@ export class BTree extends Mapping {
720
701
  * @param high This method returns when a key larger than this is reached.
721
702
  * @param includeHigh If the `high` key is present, its pair will be included
722
703
  * in the output if and only if this parameter is true. Note: if the
723
- * `low` key is present, it is always included in the output.
724
- * @param maxLength Length limit. getRange will stop scanning the tree when
704
+ * `low` key is present, it is included in the output unless `low`
705
+ * equals `high` and this parameter is false.
706
+ * @param maxLength Length limit. keysRange will stop scanning the tree when
725
707
  * the array reaches this size.
726
708
  * @description Computational complexity: O(result.length + log size)
727
709
  */
728
710
  keysRange(low, high, includeHigh, maxLength = 0x3ffffff) {
729
711
  const results = [];
730
712
  this.root.forRange(low, high, includeHigh, false, this, 0, (k, v) => {
713
+ if (results.length >= maxLength)
714
+ return Break;
731
715
  results.push([k, v]);
732
- return results.length > maxLength ? Break : undefined;
733
716
  });
734
717
  return results;
735
718
  }
@@ -743,8 +726,8 @@ export class BTree extends Mapping {
743
726
  extend(entries) {
744
727
  let added = 0;
745
728
  for (const pair of entries) {
746
- this.set(pair[0], pair[1] ?? null);
747
- added++;
729
+ if (this.set(pair[0], pair[1] ?? null))
730
+ added++;
748
731
  }
749
732
  return added;
750
733
  }
@@ -786,7 +769,7 @@ export class BTree extends Mapping {
786
769
  * function can return `{value:v}` to change the value associated
787
770
  * with the current key, `{delete:true}` to delete the current pair,
788
771
  * `{done:R}` to stop early with result R, or it can return nothing
789
- * (undefined or {}) to cause no effect and continue iterating.
772
+ * (undefined, null or {}) to cause no effect and continue iterating.
790
773
  * `{done:R}` can be combined with one of the other two commands.
791
774
  * The third argument `counter` is the number of items iterated
792
775
  * previously; it equals 0 when `onFound` is called the first time.
@@ -850,18 +833,17 @@ export class BTree extends Mapping {
850
833
  return height;
851
834
  }
852
835
  /** Makes the object read-only to ensure it is not accidentally modified.
853
- * Freezing does not have to be permanent; unfreeze() reverses the effect.
854
836
  * This is accomplished by replacing mutator functions with a function
855
837
  * that throws an Error. Compared to using a property (e.g. this.isFrozen)
856
838
  * this implementation gives better performance in non-frozen BTrees.
857
839
  */
858
840
  freeze() {
859
841
  const t = this;
860
- // Note: all other mutators ultimately call set() or editRange()
842
+ // Note: all other mutators ultimately call set() or rangeUpdate()
861
843
  // so we don't need to override those others.
862
844
  t.clear =
863
845
  t.set =
864
- t.editRange =
846
+ t.rangeUpdate =
865
847
  () => {
866
848
  throw TypeError('Attempted to modify a frozen BTree');
867
849
  };
@@ -918,12 +900,12 @@ class BNodeInternal extends BNode {
918
900
  return this.children[0].minPair(reusedArray);
919
901
  }
920
902
  maxPair(reusedArray) {
921
- return this.children[this.children.length - 1].maxPair(reusedArray);
903
+ return this.children.at(-1).maxPair(reusedArray);
922
904
  }
923
905
  get(key, defaultValue, tree) {
924
906
  const i = this.indexOf(key, 0, tree._compare);
925
907
  const children = this.children;
926
- return i < children.length ? children[i].get(key, defaultValue, tree) : undefined;
908
+ return i < children.length ? children[i].get(key, defaultValue, tree) : defaultValue;
927
909
  }
928
910
  getPairOrNextLower(key, compare, inclusive, reusedArray) {
929
911
  const i = this.indexOf(key, 0, compare);
@@ -1006,9 +988,9 @@ class BNodeInternal extends BNode {
1006
988
  }
1007
989
  }
1008
990
  const result = child.set(key, value, overwrite, tree);
991
+ this.keys[i] = child.maxKey();
1009
992
  if (result === false)
1010
993
  return false;
1011
- this.keys[i] = child.maxKey();
1012
994
  if (result === true)
1013
995
  return true;
1014
996
  // The child has split and `result` is a new right child... does it fit?
@@ -1051,18 +1033,14 @@ class BNodeInternal extends BNode {
1051
1033
  // Reminder: parent node must update its copy of key for this node
1052
1034
  // assert: neither node is shared
1053
1035
  // assert rhs.keys.length > (maxNodeSize/2 && this.keys.length<maxNodeSize)
1054
- // eslint-disable-next-line
1055
1036
  this.keys.push(rhs.keys.shift());
1056
- // eslint-disable-next-line
1057
1037
  this.children.push(rhs.children.shift());
1058
1038
  }
1059
1039
  takeFromLeft(lhs) {
1060
1040
  // Reminder: parent node must update its copy of key for this node
1061
1041
  // assert: neither node is shared
1062
1042
  // assert rhs.keys.length > (maxNodeSize/2 && this.keys.length<maxNodeSize)
1063
- // eslint-disable-next-line
1064
1043
  this.keys.unshift(lhs.keys.pop());
1065
- // eslint-disable-next-line
1066
1044
  this.children.unshift(lhs.children.pop());
1067
1045
  }
1068
1046
  /// //////////////////////////////////////////////////////////////////////////
@@ -87,7 +87,7 @@ export class Heap extends Collection {
87
87
  * @return `true` if the item was removed.
88
88
  */
89
89
  remove(item) {
90
- const index = this.heap.indexOf(item);
90
+ const index = Number.isNaN(item) ? this.heap.findIndex(Number.isNaN) : this.heap.indexOf(item);
91
91
  if (index < 0)
92
92
  return false;
93
93
  if (index === 0) {