AppendOnlyStackedSet.js 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Ivan Kopeykin @vankop
  4. */
  5. "use strict";
  6. /**
  7. * Tracks values across a stack of nested sets where child scopes can add new
  8. * values without mutating the sets created by their parents.
  9. * @template T
  10. */
  11. class AppendOnlyStackedSet {
  12. /**
  13. * Seeds the stacked set with an optional chain of previously created scope
  14. * layers.
  15. * @param {Set<T>[]} sets an optional array of sets
  16. */
  17. constructor(sets = []) {
  18. /** @type {Set<T>[]} */
  19. this._sets = sets;
  20. /** @type {Set<T> | undefined} */
  21. this._current = undefined;
  22. }
  23. /**
  24. * Adds a value to the current scope layer, creating that layer lazily when
  25. * the first write occurs.
  26. * @param {T} el element
  27. */
  28. add(el) {
  29. if (!this._current) {
  30. this._current = new Set();
  31. this._sets.push(this._current);
  32. }
  33. this._current.add(el);
  34. }
  35. /**
  36. * Checks whether a value is present in any scope layer currently visible to
  37. * this stacked set.
  38. * @param {T} el element
  39. * @returns {boolean} result
  40. */
  41. has(el) {
  42. for (const set of this._sets) {
  43. if (set.has(el)) return true;
  44. }
  45. return false;
  46. }
  47. /**
  48. * Removes every scope layer and any values accumulated in them.
  49. */
  50. clear() {
  51. this._sets = [];
  52. if (this._current) {
  53. this._current = undefined;
  54. }
  55. }
  56. /**
  57. * Creates a child stacked set that shares the existing scope history while
  58. * allowing subsequent additions to be recorded in its own new layer.
  59. * @returns {AppendOnlyStackedSet<T>} child
  60. */
  61. createChild() {
  62. return new AppendOnlyStackedSet(this._sets.length ? [...this._sets] : []);
  63. }
  64. /**
  65. * Iterates over the stacked sets from newest to oldest so consumers can
  66. * inspect recently added values first.
  67. * @returns {Iterator<T>} iterable iterator
  68. */
  69. [Symbol.iterator]() {
  70. const iterators = this._sets.map((map) => map[Symbol.iterator]());
  71. let current = iterators.pop();
  72. return {
  73. next() {
  74. if (!current) return { done: true, value: undefined };
  75. let result = current.next();
  76. while (result.done && iterators.length > 0) {
  77. current = /** @type {SetIterator<T>} */ (iterators.pop());
  78. result = current.next();
  79. }
  80. return result;
  81. }
  82. };
  83. }
  84. }
  85. module.exports = AppendOnlyStackedSet;