@tylerbu/sorted-btree-es6
Version:
A sorted list of key-value pairs in a fast, typed in-memory B+ tree with a powerful API.
154 lines (153 loc) • 8.84 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.BTreeEx = void 0;
const b_tree_1 = require("../b+tree");
const diffAgainst_1 = require("./diffAgainst");
const forEachKeyInBoth_1 = require("./forEachKeyInBoth");
const forEachKeyNotIn_1 = require("./forEachKeyNotIn");
const intersect_1 = require("./intersect");
const subtract_1 = require("./subtract");
const union_1 = require("./union");
const bulkLoad_1 = require("./bulkLoad");
/**
* An extended version of the `BTree` class that includes additional functionality
* such as bulk loading, set operations, and diffing.
* It is separated to keep the core BTree class small from a bundle size perspective.
* Note: each additional functionality piece is available as a standalone function from the extended folder.
* @extends BTree
*/
class BTreeEx extends b_tree_1.BTree {
/**
* Bulk loads a new `BTreeEx` from parallel arrays of sorted entries.
* This reuses the same algorithm as `extended/bulkLoad`, but produces a `BTreeEx`.
* Time and space complexity are O(n).
* @param keys Keys to load, sorted by key in strictly ascending order.
* @param values Values aligned with the supplied keys.
* @param maxNodeSize The branching factor (maximum number of children per node).
* @param compare Comparator to use. Defaults to the standard comparator if omitted.
* @returns A fully built tree containing the supplied entries.
* @throws Error if the entries are not strictly sorted or contain duplicate keys.
*/
static bulkLoad(keys, values, maxNodeSize, compare) {
const cmp = compare ?? b_tree_1.defaultComparator;
const root = (0, bulkLoad_1.bulkLoadRoot)(keys, values, maxNodeSize, cmp);
const tree = new BTreeEx(undefined, cmp, maxNodeSize);
const target = tree;
target._root = root;
return tree;
}
/** See {@link BTree.clone}. */
clone() {
const source = this;
source._root.isShared = true;
const result = new BTreeEx(undefined, this._compare, this._maxNodeSize);
const target = result;
target._root = source._root;
return result;
}
/** See {@link BTree.greedyClone}. */
greedyClone(force) {
const source = this;
const result = new BTreeEx(undefined, this._compare, this._maxNodeSize);
const target = result;
target._root = source._root.greedyClone(force);
return result;
}
/**
* Computes the differences between `this` and `other`.
* For efficiency, the diff is returned via invocations of supplied handlers.
* The computation is optimized for the case in which the two trees have large amounts of shared data
* (obtained by calling the `clone` or `with` APIs) and will avoid any iteration of shared state.
* The handlers can cause computation to early exit by returning `{ break: R }`.
* Neither collection should be mutated during the comparison (inside your callbacks), as this method assumes they remain stable.
* @param other The tree to compute a diff against.
* @param onlyThis Callback invoked for all keys only present in `this`.
* @param onlyOther Callback invoked for all keys only present in `other`.
* @param different Callback invoked for all keys with differing values.
* @returns The first `break` payload returned by a handler, or `undefined` if no handler breaks.
* @throws Error if the supplied trees were created with different comparators.
*/
diffAgainst(other, onlyThis, onlyOther, different) {
return (0, diffAgainst_1.diffAgainst)(this, other, onlyThis, onlyOther, different);
}
/**
* Calls the supplied `callback` for each key/value pair shared by this tree and `other`, in sorted key order.
* Neither tree is modified.
*
* Complexity is O(N + M) when the trees overlap heavily, and additionally bounded by O(log(N + M) * D)
* where `D` is the number of disjoint key ranges between the trees, because disjoint subtrees are skipped.
* In practice, that means for keys of random distribution the performance is linear and for keys with significant
* numbers of non-overlapping key ranges it is much faster.
* @param other The other tree to compare with this one.
* @param callback Called for keys that appear in both trees. It can cause iteration to early exit by returning `{ break: R }`.
* @returns The first `break` payload returned by the callback, or `undefined` if the walk finishes.
* @throws Error if the two trees were created with different comparators.
*/
forEachKeyInBoth(other, callback) {
return (0, forEachKeyInBoth_1.forEachKeyInBoth)(this, other, callback);
}
/**
* Calls the supplied `callback` for each key/value pair that exists in this tree but not in `other`
* (set subtraction). The callback runs in sorted key order and neither tree is modified.
*
* Complexity is O(N + M) when the key ranges overlap heavily, and additionally bounded by O(log(N + M) * D)
* where `D` is the number of disjoint ranges between the trees, because non-overlapping subtrees are skipped.
* In practice, that means for keys of random distribution the performance is linear and for keys with significant
* numbers of non-overlapping key ranges it is much faster.
* @param other Keys present in this tree will be omitted from the callback.
* @param callback Invoked for keys unique to `this`. It can cause iteration to early exit by returning `{ break: R }`.
* @returns The first `break` payload returned by the callback, or `undefined` if all qualifying keys are visited.
* @throws Error if the trees were created with different comparators.
*/
forEachKeyNotIn(other, callback) {
return (0, forEachKeyNotIn_1.forEachKeyNotIn)(this, other, callback);
}
/**
* Returns a new tree containing only keys present in both trees.
* Neither tree is modified.
*
* Complexity is O(N + M) in the fully overlapping case and additionally bounded by O(log(N + M) * D),
* where `D` is the number of disjoint key ranges, because disjoint subtrees are skipped entirely.
* In practice, that means for keys of random distribution the performance is linear and for keys with significant
* numbers of non-overlapping key ranges it is much faster.
* @param other The other tree to intersect with this one.
* @param combineFn Called for keys that appear in both trees. Return the desired value.
* @returns A new `BTreeEx` populated with the intersection.
* @throws Error if the trees were created with different comparators.
*/
intersect(other, combineFn) {
return (0, intersect_1.intersect)(this, other, combineFn);
}
/**
* Efficiently unions this tree with `other`, reusing subtrees wherever possible without modifying either input.
*
* Complexity is O(N + M) in the fully overlapping case, and additionally bounded by O(log(N + M) * D)
* where `D` is the number of disjoint key ranges, because disjoint subtrees are skipped entirely.
* In practice, that means for keys of random distribution the performance is linear and for keys with significant
* numbers of non-overlapping key ranges it is much faster.
* @param other The other tree to union with this one.
* @param combineFn Called for keys that appear in both trees. Return the desired value, or `undefined` to omit the key.
* @returns A new `BTreeEx` that contains the unioned key/value pairs.
* @throws Error if the trees were created with different comparators or max node sizes.
*/
union(other, combineFn) {
return (0, union_1.union)(this, other, combineFn);
}
/**
* Returns a new tree containing only the keys that are present in this tree but not `other` (set subtraction).
* Neither input tree is modified.
*
* Complexity is O(N + M) for time and O(N) for allocations in the worst case. Additionally, time is bounded by
* O(log(N + M) * D1) and space by O(log N * D2) where `D1` is the number of disjoint key ranges between the trees
* and `D2` is the number of disjoint ranges inside this tree.
* In practice, that means for keys of random distribution the performance is linear and for keys with significant
* numbers of non-overlapping key ranges it is much faster.
* @param other The tree whose keys will be removed from the result.
* @returns A new `BTreeEx` representing `this \ other`.
* @throws Error if the trees were created with different comparators or max node sizes.
*/
subtract(other) {
return (0, subtract_1.subtract)(this, other);
}
}
exports.BTreeEx = BTreeEx;