SortableSet.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const NONE = Symbol("not sorted");
  7. /**
  8. * A subset of Set that offers sorting functionality
  9. * @template T item type in set
  10. * @extends {Set<T>}
  11. */
  12. class SortableSet extends Set {
  13. /**
  14. * Create a new sortable set
  15. * @template T
  16. * @param {Iterable<T>=} initialIterable The initial iterable value
  17. * @typedef {function(T, T): number} SortFunction
  18. * @param {SortFunction<T>=} defaultSort Default sorting function
  19. */
  20. constructor(initialIterable, defaultSort) {
  21. super(initialIterable);
  22. /**
  23. * @private
  24. * @type {undefined | SortFunction<T>}
  25. */
  26. this._sortFn = defaultSort;
  27. /**
  28. * @private
  29. * @type {typeof NONE | undefined | function(T, T): number}}
  30. */
  31. this._lastActiveSortFn = NONE;
  32. /**
  33. * @private
  34. * @type {Map<Function, any> | undefined}
  35. */
  36. this._cache = undefined;
  37. /**
  38. * @private
  39. * @type {Map<Function, any> | undefined}
  40. */
  41. this._cacheOrderIndependent = undefined;
  42. }
  43. /**
  44. * @param {T} value value to add to set
  45. * @returns {this} returns itself
  46. */
  47. add(value) {
  48. this._lastActiveSortFn = NONE;
  49. this._invalidateCache();
  50. this._invalidateOrderedCache();
  51. super.add(value);
  52. return this;
  53. }
  54. /**
  55. * @param {T} value value to delete
  56. * @returns {boolean} true if value existed in set, false otherwise
  57. */
  58. delete(value) {
  59. this._invalidateCache();
  60. this._invalidateOrderedCache();
  61. return super.delete(value);
  62. }
  63. /**
  64. * @returns {void}
  65. */
  66. clear() {
  67. this._invalidateCache();
  68. this._invalidateOrderedCache();
  69. return super.clear();
  70. }
  71. /**
  72. * Sort with a comparer function
  73. * @param {SortFunction<T> | undefined} sortFn Sorting comparer function
  74. * @returns {void}
  75. */
  76. sortWith(sortFn) {
  77. if (this.size <= 1 || sortFn === this._lastActiveSortFn) {
  78. // already sorted - nothing to do
  79. return;
  80. }
  81. const sortedArray = Array.from(this).sort(sortFn);
  82. super.clear();
  83. for (let i = 0; i < sortedArray.length; i += 1) {
  84. super.add(sortedArray[i]);
  85. }
  86. this._lastActiveSortFn = sortFn;
  87. this._invalidateCache();
  88. }
  89. sort() {
  90. this.sortWith(this._sortFn);
  91. return this;
  92. }
  93. /**
  94. * Get data from cache
  95. * @template R
  96. * @param {function(SortableSet<T>): R} fn function to calculate value
  97. * @returns {R} returns result of fn(this), cached until set changes
  98. */
  99. getFromCache(fn) {
  100. if (this._cache === undefined) {
  101. this._cache = new Map();
  102. } else {
  103. const result = this._cache.get(fn);
  104. const data = /** @type {R} */ (result);
  105. if (data !== undefined) {
  106. return data;
  107. }
  108. }
  109. const newData = fn(this);
  110. this._cache.set(fn, newData);
  111. return newData;
  112. }
  113. /**
  114. * Get data from cache (ignoring sorting)
  115. * @template R
  116. * @param {function(SortableSet<T>): R} fn function to calculate value
  117. * @returns {R} returns result of fn(this), cached until set changes
  118. */
  119. getFromUnorderedCache(fn) {
  120. if (this._cacheOrderIndependent === undefined) {
  121. this._cacheOrderIndependent = new Map();
  122. } else {
  123. const result = this._cacheOrderIndependent.get(fn);
  124. const data = /** @type {R} */ (result);
  125. if (data !== undefined) {
  126. return data;
  127. }
  128. }
  129. const newData = fn(this);
  130. this._cacheOrderIndependent.set(fn, newData);
  131. return newData;
  132. }
  133. /**
  134. * @private
  135. * @returns {void}
  136. */
  137. _invalidateCache() {
  138. if (this._cache !== undefined) {
  139. this._cache.clear();
  140. }
  141. }
  142. /**
  143. * @private
  144. * @returns {void}
  145. */
  146. _invalidateOrderedCache() {
  147. if (this._cacheOrderIndependent !== undefined) {
  148. this._cacheOrderIndependent.clear();
  149. }
  150. }
  151. /**
  152. * @returns {T[]} the raw array
  153. */
  154. toJSON() {
  155. return Array.from(this);
  156. }
  157. }
  158. module.exports = SortableSet;