LazyBucketSortedSet.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { first } = require("./SetHelpers");
  7. const SortableSet = require("./SortableSet");
  8. /**
  9. * @template T
  10. * @template K
  11. * @typedef {(item: T) => K} GetKey
  12. */
  13. /**
  14. * @template T
  15. * @typedef {(a: T, n: T) => number} Comparator
  16. */
  17. /**
  18. * @template T
  19. * @template K
  20. * @typedef {LazyBucketSortedSet<T, K> | SortableSet<T>} Entry
  21. */
  22. /**
  23. * @template T
  24. * @template K
  25. * @typedef {GetKey<T, K> | Comparator<K> | Comparator<T>} Arg
  26. */
  27. /**
  28. * Multi layer bucket sorted set:
  29. * Supports adding non-existing items (DO NOT ADD ITEM TWICE),
  30. * Supports removing exiting items (DO NOT REMOVE ITEM NOT IN SET),
  31. * Supports popping the first items according to defined order,
  32. * Supports iterating all items without order,
  33. * Supports updating an item in an efficient way,
  34. * Supports size property, which is the number of items,
  35. * Items are lazy partially sorted when needed
  36. * @template T
  37. * @template K
  38. */
  39. class LazyBucketSortedSet {
  40. /**
  41. * @param {GetKey<T, K>} getKey function to get key from item
  42. * @param {Comparator<K>=} comparator comparator to sort keys
  43. * @param {...Arg<T, K>} args more pairs of getKey and comparator plus optional final comparator for the last layer
  44. */
  45. constructor(getKey, comparator, ...args) {
  46. this._getKey = getKey;
  47. this._innerArgs = args;
  48. this._leaf = args.length <= 1;
  49. this._keys = new SortableSet(undefined, comparator);
  50. /** @type {Map<K, Entry<T, K>>} */
  51. this._map = new Map();
  52. this._unsortedItems = new Set();
  53. this.size = 0;
  54. }
  55. /**
  56. * @param {T} item an item
  57. * @returns {void}
  58. */
  59. add(item) {
  60. this.size++;
  61. this._unsortedItems.add(item);
  62. }
  63. /**
  64. * @param {K} key key of item
  65. * @param {T} item the item
  66. * @returns {void}
  67. */
  68. _addInternal(key, item) {
  69. let entry = this._map.get(key);
  70. if (entry === undefined) {
  71. entry = this._leaf
  72. ? new SortableSet(
  73. undefined,
  74. /** @type {Comparator<T>} */
  75. (this._innerArgs[0])
  76. )
  77. : new LazyBucketSortedSet(
  78. .../** @type {[GetKey<T, K>, Comparator<K>]} */
  79. (this._innerArgs)
  80. );
  81. this._keys.add(key);
  82. this._map.set(key, entry);
  83. }
  84. entry.add(item);
  85. }
  86. /**
  87. * @param {T} item an item
  88. * @returns {void}
  89. */
  90. delete(item) {
  91. this.size--;
  92. if (this._unsortedItems.has(item)) {
  93. this._unsortedItems.delete(item);
  94. return;
  95. }
  96. const key = this._getKey(item);
  97. const entry = /** @type {Entry<T, K>} */ (this._map.get(key));
  98. entry.delete(item);
  99. if (entry.size === 0) {
  100. this._deleteKey(key);
  101. }
  102. }
  103. /**
  104. * @param {K} key key to be removed
  105. * @returns {void}
  106. */
  107. _deleteKey(key) {
  108. this._keys.delete(key);
  109. this._map.delete(key);
  110. }
  111. /**
  112. * @returns {T | undefined} an item
  113. */
  114. popFirst() {
  115. if (this.size === 0) return;
  116. this.size--;
  117. if (this._unsortedItems.size > 0) {
  118. for (const item of this._unsortedItems) {
  119. const key = this._getKey(item);
  120. this._addInternal(key, item);
  121. }
  122. this._unsortedItems.clear();
  123. }
  124. this._keys.sort();
  125. const key = /** @type {K} */ (first(this._keys));
  126. const entry = this._map.get(key);
  127. if (this._leaf) {
  128. const leafEntry = /** @type {SortableSet<T>} */ (entry);
  129. leafEntry.sort();
  130. const item = /** @type {T} */ (first(leafEntry));
  131. leafEntry.delete(item);
  132. if (leafEntry.size === 0) {
  133. this._deleteKey(key);
  134. }
  135. return item;
  136. }
  137. const nodeEntry =
  138. /** @type {LazyBucketSortedSet<T, K>} */
  139. (entry);
  140. const item = nodeEntry.popFirst();
  141. if (nodeEntry.size === 0) {
  142. this._deleteKey(key);
  143. }
  144. return item;
  145. }
  146. /**
  147. * @param {T} item to be updated item
  148. * @returns {(remove?: true) => void} finish update
  149. */
  150. startUpdate(item) {
  151. if (this._unsortedItems.has(item)) {
  152. return remove => {
  153. if (remove) {
  154. this._unsortedItems.delete(item);
  155. this.size--;
  156. }
  157. };
  158. }
  159. const key = this._getKey(item);
  160. if (this._leaf) {
  161. const oldEntry = /** @type {SortableSet<T>} */ (this._map.get(key));
  162. return remove => {
  163. if (remove) {
  164. this.size--;
  165. oldEntry.delete(item);
  166. if (oldEntry.size === 0) {
  167. this._deleteKey(key);
  168. }
  169. return;
  170. }
  171. const newKey = this._getKey(item);
  172. if (key === newKey) {
  173. // This flags the sortable set as unordered
  174. oldEntry.add(item);
  175. } else {
  176. oldEntry.delete(item);
  177. if (oldEntry.size === 0) {
  178. this._deleteKey(key);
  179. }
  180. this._addInternal(newKey, item);
  181. }
  182. };
  183. }
  184. const oldEntry =
  185. /** @type {LazyBucketSortedSet<T, K>} */
  186. (this._map.get(key));
  187. const finishUpdate = oldEntry.startUpdate(item);
  188. return remove => {
  189. if (remove) {
  190. this.size--;
  191. finishUpdate(true);
  192. if (oldEntry.size === 0) {
  193. this._deleteKey(key);
  194. }
  195. return;
  196. }
  197. const newKey = this._getKey(item);
  198. if (key === newKey) {
  199. finishUpdate();
  200. } else {
  201. finishUpdate(true);
  202. if (oldEntry.size === 0) {
  203. this._deleteKey(key);
  204. }
  205. this._addInternal(newKey, item);
  206. }
  207. };
  208. }
  209. /**
  210. * @param {Iterator<T>[]} iterators list of iterators to append to
  211. * @returns {void}
  212. */
  213. _appendIterators(iterators) {
  214. if (this._unsortedItems.size > 0)
  215. iterators.push(this._unsortedItems[Symbol.iterator]());
  216. for (const key of this._keys) {
  217. const entry = this._map.get(key);
  218. if (this._leaf) {
  219. const leafEntry = /** @type {SortableSet<T>} */ (entry);
  220. const iterator = leafEntry[Symbol.iterator]();
  221. iterators.push(iterator);
  222. } else {
  223. const nodeEntry =
  224. /** @type {LazyBucketSortedSet<T, K>} */
  225. (entry);
  226. nodeEntry._appendIterators(iterators);
  227. }
  228. }
  229. }
  230. /**
  231. * @returns {Iterator<T>} the iterator
  232. */
  233. [Symbol.iterator]() {
  234. /** @type {Iterator<T>[]} */
  235. const iterators = [];
  236. this._appendIterators(iterators);
  237. iterators.reverse();
  238. let currentIterator =
  239. /** @type {Iterator<T>} */
  240. (iterators.pop());
  241. return {
  242. next: () => {
  243. const res = currentIterator.next();
  244. if (res.done) {
  245. if (iterators.length === 0) return res;
  246. currentIterator = /** @type {Iterator<T>} */ (iterators.pop());
  247. return currentIterator.next();
  248. }
  249. return res;
  250. }
  251. };
  252. }
  253. }
  254. module.exports = LazyBucketSortedSet;