| "use strict"; |
| |
| /** |
| * A subset of Set that offers sorting functionality |
| * @template T item type in set |
| * @extends {Set<T>} |
| */ |
| class SortableSet extends Set { |
| /** |
| * Create a new sortable set |
| * @param {Iterable<T>=} initialIterable The initial iterable value |
| * @typedef {function(T, T): number} SortFunction |
| * @param {SortFunction=} defaultSort Default sorting function |
| */ |
| constructor(initialIterable, defaultSort) { |
| super(initialIterable); |
| /** @private @type {function(T, T): number}} */ |
| this._sortFn = defaultSort; |
| /** @private @type {function(T, T): number} | null} */ |
| this._lastActiveSortFn = null; |
| /** @private @type {Map<Function, T[]> | undefined} */ |
| this._cache = undefined; |
| /** @private @type {Map<Function, T[]|string|number> | undefined} */ |
| this._cacheOrderIndependent = undefined; |
| } |
| |
| /** |
| * @param {T} value value to add to set |
| * @returns {this} returns itself |
| */ |
| add(value) { |
| this._lastActiveSortFn = null; |
| this._invalidateCache(); |
| this._invalidateOrderedCache(); |
| super.add(value); |
| return this; |
| } |
| |
| /** |
| * @param {T} value value to delete |
| * @returns {boolean} true if value existed in set, false otherwise |
| */ |
| delete(value) { |
| this._invalidateCache(); |
| this._invalidateOrderedCache(); |
| return super.delete(value); |
| } |
| |
| /** |
| * @returns {void} |
| */ |
| clear() { |
| this._invalidateCache(); |
| this._invalidateOrderedCache(); |
| return super.clear(); |
| } |
| |
| /** |
| * Sort with a comparer function |
| * @param {SortFunction} sortFn Sorting comparer function |
| * @returns {void} |
| */ |
| sortWith(sortFn) { |
| if (this.size <= 1 || sortFn === this._lastActiveSortFn) { |
| // already sorted - nothing to do |
| return; |
| } |
| |
| const sortedArray = Array.from(this).sort(sortFn); |
| super.clear(); |
| for (let i = 0; i < sortedArray.length; i += 1) { |
| super.add(sortedArray[i]); |
| } |
| this._lastActiveSortFn = sortFn; |
| this._invalidateCache(); |
| } |
| |
| sort() { |
| this.sortWith(this._sortFn); |
| } |
| |
| /** |
| * Get data from cache |
| * @param {function(SortableSet<T>): T[]} fn function to calculate value |
| * @returns {T[]} returns result of fn(this), cached until set changes |
| */ |
| getFromCache(fn) { |
| if (this._cache === undefined) { |
| this._cache = new Map(); |
| } else { |
| const data = this._cache.get(fn); |
| if (data !== undefined) { |
| return data; |
| } |
| } |
| const newData = fn(this); |
| this._cache.set(fn, newData); |
| return newData; |
| } |
| |
| /** |
| * @param {function(SortableSet<T>): string|number|T[]} fn function to calculate value |
| * @returns {any} returns result of fn(this), cached until set changes |
| */ |
| getFromUnorderedCache(fn) { |
| if (this._cacheOrderIndependent === undefined) { |
| this._cacheOrderIndependent = new Map(); |
| } else { |
| const data = this._cacheOrderIndependent.get(fn); |
| if (data !== undefined) { |
| return data; |
| } |
| } |
| const newData = fn(this); |
| this._cacheOrderIndependent.set(fn, newData); |
| return newData; |
| } |
| |
| /** |
| * @private |
| * @returns {void} |
| */ |
| _invalidateCache() { |
| if (this._cache !== undefined) { |
| this._cache.clear(); |
| } |
| } |
| |
| /** |
| * @private |
| * @returns {void} |
| */ |
| _invalidateOrderedCache() { |
| if (this._cacheOrderIndependent !== undefined) { |
| this._cacheOrderIndependent.clear(); |
| } |
| } |
| } |
| |
| module.exports = SortableSet; |