stringDictionary.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import { Nullable } from "../types";
  2. /**
  3. * This class implement a typical dictionary using a string as key and the generic type T as value.
  4. * The underlying implementation relies on an associative array to ensure the best performances.
  5. * The value can be anything including 'null' but except 'undefined'
  6. */
  7. export class StringDictionary<T> {
  8. /**
  9. * This will clear this dictionary and copy the content from the 'source' one.
  10. * If the T value is a custom object, it won't be copied/cloned, the same object will be used
  11. * @param source the dictionary to take the content from and copy to this dictionary
  12. */
  13. public copyFrom(source: StringDictionary<T>) {
  14. this.clear();
  15. source.forEach((t, v) => this.add(t, v));
  16. }
  17. /**
  18. * Get a value based from its key
  19. * @param key the given key to get the matching value from
  20. * @return the value if found, otherwise undefined is returned
  21. */
  22. public get(key: string): T | undefined {
  23. var val = this._data[key];
  24. if (val !== undefined) {
  25. return val;
  26. }
  27. return undefined;
  28. }
  29. /**
  30. * Get a value from its key or add it if it doesn't exist.
  31. * This method will ensure you that a given key/data will be present in the dictionary.
  32. * @param key the given key to get the matching value from
  33. * @param factory the factory that will create the value if the key is not present in the dictionary.
  34. * The factory will only be invoked if there's no data for the given key.
  35. * @return the value corresponding to the key.
  36. */
  37. public getOrAddWithFactory(key: string, factory: (key: string) => T): T {
  38. var val = this.get(key);
  39. if (val !== undefined) {
  40. return val;
  41. }
  42. val = factory(key);
  43. if (val) {
  44. this.add(key, val);
  45. }
  46. return val;
  47. }
  48. /**
  49. * Get a value from its key if present in the dictionary otherwise add it
  50. * @param key the key to get the value from
  51. * @param val if there's no such key/value pair in the dictionary add it with this value
  52. * @return the value corresponding to the key
  53. */
  54. public getOrAdd(key: string, val: T): T {
  55. var curVal = this.get(key);
  56. if (curVal !== undefined) {
  57. return curVal;
  58. }
  59. this.add(key, val);
  60. return val;
  61. }
  62. /**
  63. * Check if there's a given key in the dictionary
  64. * @param key the key to check for
  65. * @return true if the key is present, false otherwise
  66. */
  67. public contains(key: string): boolean {
  68. return this._data[key] !== undefined;
  69. }
  70. /**
  71. * Add a new key and its corresponding value
  72. * @param key the key to add
  73. * @param value the value corresponding to the key
  74. * @return true if the operation completed successfully, false if we couldn't insert the key/value because there was already this key in the dictionary
  75. */
  76. public add(key: string, value: T): boolean {
  77. if (this._data[key] !== undefined) {
  78. return false;
  79. }
  80. this._data[key] = value;
  81. ++this._count;
  82. return true;
  83. }
  84. /**
  85. * Update a specific value associated to a key
  86. * @param key defines the key to use
  87. * @param value defines the value to store
  88. * @returns true if the value was updated (or false if the key was not found)
  89. */
  90. public set(key: string, value: T): boolean {
  91. if (this._data[key] === undefined) {
  92. return false;
  93. }
  94. this._data[key] = value;
  95. return true;
  96. }
  97. /**
  98. * Get the element of the given key and remove it from the dictionary
  99. * @param key defines the key to search
  100. * @returns the value associated with the key or null if not found
  101. */
  102. public getAndRemove(key: string): Nullable<T> {
  103. let val = this.get(key);
  104. if (val !== undefined) {
  105. delete this._data[key];
  106. --this._count;
  107. return val;
  108. }
  109. return null;
  110. }
  111. /**
  112. * Remove a key/value from the dictionary.
  113. * @param key the key to remove
  114. * @return true if the item was successfully deleted, false if no item with such key exist in the dictionary
  115. */
  116. public remove(key: string): boolean {
  117. if (this.contains(key)) {
  118. delete this._data[key];
  119. --this._count;
  120. return true;
  121. }
  122. return false;
  123. }
  124. /**
  125. * Clear the whole content of the dictionary
  126. */
  127. public clear() {
  128. this._data = {};
  129. this._count = 0;
  130. }
  131. /**
  132. * Gets the current count
  133. */
  134. public get count() {
  135. return this._count;
  136. }
  137. /**
  138. * Execute a callback on each key/val of the dictionary.
  139. * Note that you can remove any element in this dictionary in the callback implementation
  140. * @param callback the callback to execute on a given key/value pair
  141. */
  142. public forEach(callback: (key: string, val: T) => void) {
  143. for (let cur in this._data) {
  144. var val = this._data[cur];
  145. callback(cur, val);
  146. }
  147. }
  148. /**
  149. * Execute a callback on every occurrence of the dictionary until it returns a valid TRes object.
  150. * If the callback returns null or undefined the method will iterate to the next key/value pair
  151. * Note that you can remove any element in this dictionary in the callback implementation
  152. * @param callback the callback to execute, if it return a valid T instanced object the enumeration will stop and the object will be returned
  153. * @returns the first item
  154. */
  155. public first<TRes>(callback: (key: string, val: T) => TRes) {
  156. for (let cur in this._data) {
  157. var val = this._data[cur];
  158. var res = callback(cur, val);
  159. if (res) {
  160. return res;
  161. }
  162. }
  163. return null;
  164. }
  165. private _count = 0;
  166. private _data: { [key: string]: T } = {};
  167. }