multimap.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. // Copyright 2012 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview A collection similar to
  16. * {@code goog.labs.structs.Map}, but also allows associating multiple
  17. * values with a single key.
  18. *
  19. * This implementation ensures that you can use any string keys.
  20. *
  21. * @author chrishenry@google.com (Chris Henry)
  22. */
  23. goog.provide('goog.labs.structs.Multimap');
  24. goog.require('goog.array');
  25. goog.require('goog.labs.structs.Map');
  26. goog.require('goog.object');
  27. /**
  28. * Creates a new multimap.
  29. * @constructor
  30. * @struct
  31. * @final
  32. */
  33. goog.labs.structs.Multimap = function() {
  34. this.clear();
  35. };
  36. /**
  37. * The backing map.
  38. * @type {!goog.labs.structs.Map}
  39. * @private
  40. */
  41. goog.labs.structs.Multimap.prototype.map_;
  42. /**
  43. * @type {number}
  44. * @private
  45. */
  46. goog.labs.structs.Multimap.prototype.count_ = 0;
  47. /**
  48. * Clears the multimap.
  49. */
  50. goog.labs.structs.Multimap.prototype.clear = function() {
  51. this.count_ = 0;
  52. this.map_ = new goog.labs.structs.Map();
  53. };
  54. /**
  55. * Clones this multimap.
  56. * @return {!goog.labs.structs.Multimap} A multimap that contains all
  57. * the mapping this multimap has.
  58. */
  59. goog.labs.structs.Multimap.prototype.clone = function() {
  60. var map = new goog.labs.structs.Multimap();
  61. map.addAllFromMultimap(this);
  62. return map;
  63. };
  64. /**
  65. * Adds the given (key, value) pair to the map. The (key, value) pair
  66. * is guaranteed to be added.
  67. * @param {string} key The key to add.
  68. * @param {*} value The value to add.
  69. */
  70. goog.labs.structs.Multimap.prototype.add = function(key, value) {
  71. var values = this.map_.get(key);
  72. if (!values) {
  73. this.map_.set(key, (values = []));
  74. }
  75. values.push(value);
  76. this.count_++;
  77. };
  78. /**
  79. * Stores a collection of values to the given key. Does not replace
  80. * existing (key, value) pairs.
  81. * @param {string} key The key to add.
  82. * @param {!Array<*>} values The values to add.
  83. */
  84. goog.labs.structs.Multimap.prototype.addAllValues = function(key, values) {
  85. goog.array.forEach(values, function(v) { this.add(key, v); }, this);
  86. };
  87. /**
  88. * Adds the contents of the given map/multimap to this multimap.
  89. * @param {!(goog.labs.structs.Map|goog.labs.structs.Multimap)} map The
  90. * map to add.
  91. */
  92. goog.labs.structs.Multimap.prototype.addAllFromMultimap = function(map) {
  93. goog.array.forEach(map.getEntries(), function(entry) {
  94. this.add(entry[0], entry[1]);
  95. }, this);
  96. };
  97. /**
  98. * Replaces all the values for the given key with the given values.
  99. * @param {string} key The key whose values are to be replaced.
  100. * @param {!Array<*>} values The new values. If empty, this is
  101. * equivalent to {@code removaAll(key)}.
  102. */
  103. goog.labs.structs.Multimap.prototype.replaceValues = function(key, values) {
  104. this.removeAll(key);
  105. this.addAllValues(key, values);
  106. };
  107. /**
  108. * Gets the values correspond to the given key.
  109. * @param {string} key The key to retrieve.
  110. * @return {!Array<*>} An array of values corresponding to the given
  111. * key. May be empty. Note that the ordering of values are not
  112. * guaranteed to be consistent.
  113. */
  114. goog.labs.structs.Multimap.prototype.get = function(key) {
  115. var values = /** @type {Array<*>} */ (this.map_.get(key));
  116. return values ? goog.array.clone(values) : [];
  117. };
  118. /**
  119. * Removes a single occurrence of (key, value) pair.
  120. * @param {string} key The key to remove.
  121. * @param {*} value The value to remove.
  122. * @return {boolean} Whether any matching (key, value) pair is removed.
  123. */
  124. goog.labs.structs.Multimap.prototype.remove = function(key, value) {
  125. var values = /** @type {Array<*>} */ (this.map_.get(key));
  126. if (!values) {
  127. return false;
  128. }
  129. var removed = goog.array.removeIf(
  130. values, function(v) { return goog.object.is(value, v); });
  131. if (removed) {
  132. this.count_--;
  133. if (values.length == 0) {
  134. this.map_.remove(key);
  135. }
  136. }
  137. return removed;
  138. };
  139. /**
  140. * Removes all values corresponding to the given key.
  141. * @param {string} key The key whose values are to be removed.
  142. * @return {boolean} Whether any value is removed.
  143. */
  144. goog.labs.structs.Multimap.prototype.removeAll = function(key) {
  145. // We have to first retrieve the values from the backing map because
  146. // we need to keep track of count (and correctly calculates the
  147. // return value). values may be undefined.
  148. var values = this.map_.get(key);
  149. if (this.map_.remove(key)) {
  150. this.count_ -= values.length;
  151. return true;
  152. }
  153. return false;
  154. };
  155. /**
  156. * @return {boolean} Whether the multimap is empty.
  157. */
  158. goog.labs.structs.Multimap.prototype.isEmpty = function() {
  159. return !this.count_;
  160. };
  161. /**
  162. * @return {number} The count of (key, value) pairs in the map.
  163. */
  164. goog.labs.structs.Multimap.prototype.getCount = function() {
  165. return this.count_;
  166. };
  167. /**
  168. * @param {string} key The key to check.
  169. * @param {*} value The value to check.
  170. * @return {boolean} Whether the (key, value) pair exists in the multimap.
  171. */
  172. goog.labs.structs.Multimap.prototype.containsEntry = function(key, value) {
  173. var values = /** @type {Array<*>} */ (this.map_.get(key));
  174. if (!values) {
  175. return false;
  176. }
  177. var index = goog.array.findIndex(
  178. values, function(v) { return goog.object.is(v, value); });
  179. return index >= 0;
  180. };
  181. /**
  182. * @param {string} key The key to check.
  183. * @return {boolean} Whether the multimap contains at least one (key,
  184. * value) pair with the given key.
  185. */
  186. goog.labs.structs.Multimap.prototype.containsKey = function(key) {
  187. return this.map_.containsKey(key);
  188. };
  189. /**
  190. * @param {*} value The value to check.
  191. * @return {boolean} Whether the multimap contains at least one (key,
  192. * value) pair with the given value.
  193. */
  194. goog.labs.structs.Multimap.prototype.containsValue = function(value) {
  195. return goog.array.some(this.map_.getValues(), function(values) {
  196. return goog.array.some(/** @type {Array<?>} */ (values), function(v) {
  197. return goog.object.is(v, value);
  198. });
  199. });
  200. };
  201. /**
  202. * @return {!Array<string>} An array of unique keys.
  203. */
  204. goog.labs.structs.Multimap.prototype.getKeys = function() {
  205. return this.map_.getKeys();
  206. };
  207. /**
  208. * @return {!Array<*>} An array of values. There may be duplicates.
  209. */
  210. goog.labs.structs.Multimap.prototype.getValues = function() {
  211. return goog.array.flatten(this.map_.getValues());
  212. };
  213. /**
  214. * @return {!Array<!Array<?>>} An array of entries. Each entry is of the
  215. * form [key, value].
  216. */
  217. goog.labs.structs.Multimap.prototype.getEntries = function() {
  218. var keys = this.getKeys();
  219. var entries = [];
  220. for (var i = 0; i < keys.length; i++) {
  221. var key = keys[i];
  222. var values = this.get(key);
  223. for (var j = 0; j < values.length; j++) {
  224. entries.push([key, values[j]]);
  225. }
  226. }
  227. return entries;
  228. };