123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
- "use strict";
- const { first } = require("./SetHelpers");
- const SortableSet = require("./SortableSet");
- /**
- * @template T
- * @template K
- * @typedef {(item: T) => K} GetKey
- */
- /**
- * @template T
- * @typedef {(a: T, n: T) => number} Comparator
- */
- /**
- * @template T
- * @template K
- * @typedef {LazyBucketSortedSet<T, K> | SortableSet<T>} Entry
- */
- /**
- * @template T
- * @template K
- * @typedef {GetKey<T, K> | Comparator<K> | Comparator<T>} Arg
- */
- /**
- * Multi layer bucket sorted set:
- * Supports adding non-existing items (DO NOT ADD ITEM TWICE),
- * Supports removing exiting items (DO NOT REMOVE ITEM NOT IN SET),
- * Supports popping the first items according to defined order,
- * Supports iterating all items without order,
- * Supports updating an item in an efficient way,
- * Supports size property, which is the number of items,
- * Items are lazy partially sorted when needed
- * @template T
- * @template K
- */
- class LazyBucketSortedSet {
- /**
- * @param {GetKey<T, K>} getKey function to get key from item
- * @param {Comparator<K>=} comparator comparator to sort keys
- * @param {...Arg<T, K>} args more pairs of getKey and comparator plus optional final comparator for the last layer
- */
- constructor(getKey, comparator, ...args) {
- this._getKey = getKey;
- this._innerArgs = args;
- this._leaf = args.length <= 1;
- this._keys = new SortableSet(undefined, comparator);
- /** @type {Map<K, Entry<T, K>>} */
- this._map = new Map();
- this._unsortedItems = new Set();
- this.size = 0;
- }
- /**
- * @param {T} item an item
- * @returns {void}
- */
- add(item) {
- this.size++;
- this._unsortedItems.add(item);
- }
- /**
- * @param {K} key key of item
- * @param {T} item the item
- * @returns {void}
- */
- _addInternal(key, item) {
- let entry = this._map.get(key);
- if (entry === undefined) {
- entry = this._leaf
- ? new SortableSet(
- undefined,
- /** @type {Comparator<T>} */
- (this._innerArgs[0])
- )
- : new LazyBucketSortedSet(
- .../** @type {[GetKey<T, K>, Comparator<K>]} */
- (this._innerArgs)
- );
- this._keys.add(key);
- this._map.set(key, entry);
- }
- entry.add(item);
- }
- /**
- * @param {T} item an item
- * @returns {void}
- */
- delete(item) {
- this.size--;
- if (this._unsortedItems.has(item)) {
- this._unsortedItems.delete(item);
- return;
- }
- const key = this._getKey(item);
- const entry = /** @type {Entry<T, K>} */ (this._map.get(key));
- entry.delete(item);
- if (entry.size === 0) {
- this._deleteKey(key);
- }
- }
- /**
- * @param {K} key key to be removed
- * @returns {void}
- */
- _deleteKey(key) {
- this._keys.delete(key);
- this._map.delete(key);
- }
- /**
- * @returns {T | undefined} an item
- */
- popFirst() {
- if (this.size === 0) return;
- this.size--;
- if (this._unsortedItems.size > 0) {
- for (const item of this._unsortedItems) {
- const key = this._getKey(item);
- this._addInternal(key, item);
- }
- this._unsortedItems.clear();
- }
- this._keys.sort();
- const key = /** @type {K} */ (first(this._keys));
- const entry = this._map.get(key);
- if (this._leaf) {
- const leafEntry = /** @type {SortableSet<T>} */ (entry);
- leafEntry.sort();
- const item = /** @type {T} */ (first(leafEntry));
- leafEntry.delete(item);
- if (leafEntry.size === 0) {
- this._deleteKey(key);
- }
- return item;
- }
- const nodeEntry =
- /** @type {LazyBucketSortedSet<T, K>} */
- (entry);
- const item = nodeEntry.popFirst();
- if (nodeEntry.size === 0) {
- this._deleteKey(key);
- }
- return item;
- }
- /**
- * @param {T} item to be updated item
- * @returns {(remove?: true) => void} finish update
- */
- startUpdate(item) {
- if (this._unsortedItems.has(item)) {
- return remove => {
- if (remove) {
- this._unsortedItems.delete(item);
- this.size--;
- }
- };
- }
- const key = this._getKey(item);
- if (this._leaf) {
- const oldEntry = /** @type {SortableSet<T>} */ (this._map.get(key));
- return remove => {
- if (remove) {
- this.size--;
- oldEntry.delete(item);
- if (oldEntry.size === 0) {
- this._deleteKey(key);
- }
- return;
- }
- const newKey = this._getKey(item);
- if (key === newKey) {
- // This flags the sortable set as unordered
- oldEntry.add(item);
- } else {
- oldEntry.delete(item);
- if (oldEntry.size === 0) {
- this._deleteKey(key);
- }
- this._addInternal(newKey, item);
- }
- };
- }
- const oldEntry =
- /** @type {LazyBucketSortedSet<T, K>} */
- (this._map.get(key));
- const finishUpdate = oldEntry.startUpdate(item);
- return remove => {
- if (remove) {
- this.size--;
- finishUpdate(true);
- if (oldEntry.size === 0) {
- this._deleteKey(key);
- }
- return;
- }
- const newKey = this._getKey(item);
- if (key === newKey) {
- finishUpdate();
- } else {
- finishUpdate(true);
- if (oldEntry.size === 0) {
- this._deleteKey(key);
- }
- this._addInternal(newKey, item);
- }
- };
- }
- /**
- * @param {Iterator<T>[]} iterators list of iterators to append to
- * @returns {void}
- */
- _appendIterators(iterators) {
- if (this._unsortedItems.size > 0)
- iterators.push(this._unsortedItems[Symbol.iterator]());
- for (const key of this._keys) {
- const entry = this._map.get(key);
- if (this._leaf) {
- const leafEntry = /** @type {SortableSet<T>} */ (entry);
- const iterator = leafEntry[Symbol.iterator]();
- iterators.push(iterator);
- } else {
- const nodeEntry =
- /** @type {LazyBucketSortedSet<T, K>} */
- (entry);
- nodeEntry._appendIterators(iterators);
- }
- }
- }
- /**
- * @returns {Iterator<T>} the iterator
- */
- [Symbol.iterator]() {
- /** @type {Iterator<T>[]} */
- const iterators = [];
- this._appendIterators(iterators);
- iterators.reverse();
- let currentIterator =
- /** @type {Iterator<T>} */
- (iterators.pop());
- return {
- next: () => {
- const res = currentIterator.next();
- if (res.done) {
- if (iterators.length === 0) return res;
- currentIterator = /** @type {Iterator<T>} */ (iterators.pop());
- return currentIterator.next();
- }
- return res;
- }
- };
- }
- }
- module.exports = LazyBucketSortedSet;
|