TupleSet.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. /**
  7. * Nested map structure used to index tuple prefixes until the final tuple
  8. * element can be stored in a `Set`.
  9. * @template K
  10. * @template V
  11. * @typedef {Map<K, InnerMap<K, V> | Set<V>>} InnerMap
  12. */
  13. /**
  14. * Stores tuples of arbitrary length while preserving efficient prefix lookups
  15. * through a tree of maps that ends in a set of final values.
  16. * @template T
  17. * @template V
  18. */
  19. class TupleSet {
  20. /**
  21. * Seeds the tuple set with an optional iterable of tuples.
  22. * @param {Iterable<[T, V, ...EXPECTED_ANY]>=} init init
  23. */
  24. constructor(init) {
  25. /** @type {InnerMap<T, V>} */
  26. this._map = new Map();
  27. this.size = 0;
  28. if (init) {
  29. for (const tuple of init) {
  30. this.add(...tuple);
  31. }
  32. }
  33. }
  34. /**
  35. * Adds a tuple to the set, creating any missing prefix maps along the way.
  36. * @param {[T, V, ...EXPECTED_ANY]} args tuple
  37. * @returns {void}
  38. */
  39. add(...args) {
  40. let map = this._map;
  41. for (let i = 0; i < args.length - 2; i++) {
  42. const arg = args[i];
  43. const innerMap = map.get(arg);
  44. if (innerMap === undefined) {
  45. map.set(arg, (map = new Map()));
  46. } else {
  47. map = /** @type {InnerMap<T, V>} */ (innerMap);
  48. }
  49. }
  50. const beforeLast = args[args.length - 2];
  51. let set = /** @type {Set<V>} */ (map.get(beforeLast));
  52. if (set === undefined) {
  53. map.set(beforeLast, (set = new Set()));
  54. }
  55. const last = args[args.length - 1];
  56. this.size -= set.size;
  57. set.add(last);
  58. this.size += set.size;
  59. }
  60. /**
  61. * Checks whether the exact tuple is already present in the set.
  62. * @param {[T, V, ...EXPECTED_ANY]} args tuple
  63. * @returns {boolean} true, if the tuple is in the Set
  64. */
  65. has(...args) {
  66. let map = this._map;
  67. for (let i = 0; i < args.length - 2; i++) {
  68. const arg = args[i];
  69. map = /** @type {InnerMap<T, V>} */ (map.get(arg));
  70. if (map === undefined) {
  71. return false;
  72. }
  73. }
  74. const beforeLast = args[args.length - 2];
  75. const set = map.get(beforeLast);
  76. if (set === undefined) {
  77. return false;
  78. }
  79. const last = args[args.length - 1];
  80. return set.has(last);
  81. }
  82. /**
  83. * Removes a tuple from the set when it is present.
  84. * @param {[T, V, ...EXPECTED_ANY]} args tuple
  85. * @returns {void}
  86. */
  87. delete(...args) {
  88. let map = this._map;
  89. for (let i = 0; i < args.length - 2; i++) {
  90. const arg = args[i];
  91. map = /** @type {InnerMap<T, V>} */ (map.get(arg));
  92. if (map === undefined) {
  93. return;
  94. }
  95. }
  96. const beforeLast = args[args.length - 2];
  97. const set = map.get(beforeLast);
  98. if (set === undefined) {
  99. return;
  100. }
  101. const last = args[args.length - 1];
  102. this.size -= set.size;
  103. set.delete(last);
  104. this.size += set.size;
  105. }
  106. /**
  107. * Iterates over every stored tuple by walking the nested map structure and
  108. * yielding each complete prefix plus its terminal set value.
  109. * @returns {Iterator<[T, V, ...EXPECTED_ANY]>} iterator
  110. */
  111. [Symbol.iterator]() {
  112. /**
  113. * Iterator type used while traversing nested tuple-prefix maps.
  114. * @template T, V
  115. * @typedef {MapIterator<[T, InnerMap<T, V> | Set<V>]>} IteratorStack
  116. */
  117. // This is difficult to type because we can have a map inside a map inside a map, etc. where the end is a set (each key is an argument)
  118. // But in basic use we only have 2 arguments in our methods, so we have `Map<K, Set<V>>`
  119. /** @type {IteratorStack<T, V>[]} */
  120. const iteratorStack = [];
  121. /** @type {[T?, V?, ...EXPECTED_ANY]} */
  122. const tuple = [];
  123. /** @type {SetIterator<V> | undefined} */
  124. let currentSetIterator;
  125. /**
  126. * Advances through nested maps until a terminal value set is reached or
  127. * every remaining branch has been exhausted.
  128. * @param {IteratorStack<T, V>} it iterator
  129. * @returns {boolean} result
  130. */
  131. const next = (it) => {
  132. const result = it.next();
  133. if (result.done) {
  134. if (iteratorStack.length === 0) return false;
  135. tuple.pop();
  136. return next(
  137. /** @type {IteratorStack<T, V>} */
  138. (iteratorStack.pop())
  139. );
  140. }
  141. const [key, value] = result.value;
  142. iteratorStack.push(it);
  143. tuple.push(key);
  144. if (value instanceof Set) {
  145. currentSetIterator = value[Symbol.iterator]();
  146. return true;
  147. }
  148. return next(value[Symbol.iterator]());
  149. };
  150. next(this._map[Symbol.iterator]());
  151. return {
  152. next() {
  153. while (currentSetIterator) {
  154. const result = currentSetIterator.next();
  155. if (result.done) {
  156. tuple.pop();
  157. if (
  158. !next(
  159. /** @type {IteratorStack<T, V>} */
  160. (iteratorStack.pop())
  161. )
  162. ) {
  163. currentSetIterator = undefined;
  164. }
  165. } else {
  166. return {
  167. done: false,
  168. value:
  169. /* eslint-disable unicorn/prefer-spread */
  170. /** @type {[T, V, ...EXPECTED_ANY]} */
  171. (tuple.concat(result.value))
  172. };
  173. }
  174. }
  175. return { done: true, value: undefined };
  176. }
  177. };
  178. }
  179. }
  180. module.exports = TupleSet;