SortableSet.js 3.6 KB

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