UNPKG

gis-tools-ts

Version:

A collection of geospatial tools primarily designed for WGS84, Web Mercator, and S2.

145 lines 4.46 kB
/** * # Splay Tree Set * * ## Description * A splay tree set is a self-balancing binary search tree that does not allow duplicate items. * The value of a splay tree is in it's amortized O(log n) for all insert, delete min/max, * and find min/max operations. * * ## Usage * * ```ts * import { VectorSet } from 'gis-tools-ts'; * * const vecSet = new VectorSet<number>([], (a, b) => a - b); * * // If the item already exists, the existing item will be returned otherwise * // the new item will be both added and returned * let item = vecSet.add(1); * vecSet.add(2); * * console.log(vecSet.length); // 2 * // Get first and last items * let firstitem = vecSet.first(); // 1 * let lastitem = vecSet.last(); // 2 * // check if a value exists * console.log(vecSet.has(1)); // true * // look for a value right before one provided * console.log(vecSet.lastBefore(2)); // 1 * // look for a value right after one provided * console.log(vecSet.firstAfter(1)); // 2 * ``` * * ## Links * - TODO!() */ export class VectorSet { compare; #set = []; /** @param compare - compare function */ constructor(compare = (a, b) => (a < b ? -1 : a > b ? 1 : 0)) { this.compare = compare; } /** @returns - the number of items in the set */ get length() { return this.#set.length; } /** * Add an item * @param item - the item to add * @returns - the added item OR if the item already exists, the existing item */ add(item) { // if the item already exists just return that one const lowerBound = this.#lowerBound(item); if (lowerBound < this.length && this.compare(this.#set[lowerBound], item) === 0) { return this.#set[lowerBound]; } // otherwise, add, sort, and return the input item this.#set.push(item); this.#set.sort(this.compare); return item; } /** * Check if an item exists * @param key - the item * @returns - true if the item exists */ has(key) { const lowerBound = this.#lowerBound(key); return lowerBound < this.length && this.compare(this.#set[lowerBound], key) === 0; } /** * Delete an item. Return the deleted item if it exists otherwise undefined * @param key - the item * @returns - the deleted item */ delete(key) { const lowerBound = this.#lowerBound(key); if (lowerBound < this.length && this.compare(this.#set[lowerBound], key) === 0) { return this.#set.splice(lowerBound, 1)[0]; } return undefined; } /** * Get the first item * @returns - the first item, undefined if the set is empty */ first() { return this.#set.at(0); } /** * Get the last item * @returns - the last item, undefined if the set is empty */ last() { if (this.#set.length === 0) return undefined; return this.#set.at(-1); } /** * Get the last item in the set that is strictly smaller than item. * Returns undefined if no item was not found. * @param item - the item to compare against * @returns - the last item before the comparison item provided */ lastBefore(item) { const lowerBounds = this.#lowerBound(item); return this.#set[lowerBounds - 1]; } /** * Get the first item in the set that is strictly larger than item. * Returns undefined if no item was not found. * @param item - the item to compare against * @returns - the first item after the comparison item provided */ firstAfter(item) { const lowerBounds = this.#lowerBound(item); const foundItem = this.#set[lowerBounds]; if (this.compare(foundItem, item) === 0) return this.#set[lowerBounds + 1]; return foundItem; } /** * @param id - the id to search for * @returns the starting index from the lower bound of the id */ #lowerBound(id) { // lower bound search let lo = 0; let hi = this.length; let mid; while (lo < hi) { mid = Math.floor(lo + (hi - lo) / 2); const loHi = this.#set[mid]; if (this.compare(loHi, id) < 0) { lo = mid + 1; } else { hi = mid; } } return lo; } } //# sourceMappingURL=vectorSet.js.map