index.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. "use strict";
  2. /* global module, define */
  3. function mapEach(map, operation){
  4. var keys = map.keys();
  5. var next;
  6. while(!(next = keys.next()).done) {
  7. operation(map.get(next.value), next.value, map);
  8. }
  9. }
  10. var Multimap = (function() {
  11. var mapCtor;
  12. if (typeof Map !== 'undefined') {
  13. mapCtor = Map;
  14. if (!Map.prototype.keys) {
  15. Map.prototype.keys = function() {
  16. var keys = [];
  17. this.forEach(function(item, key) {
  18. keys.push(key);
  19. });
  20. return keys;
  21. };
  22. }
  23. }
  24. function Multimap(iterable) {
  25. var self = this;
  26. self._map = mapCtor;
  27. if (Multimap.Map) {
  28. self._map = Multimap.Map;
  29. }
  30. self._ = self._map ? new self._map() : {};
  31. if (iterable) {
  32. iterable.forEach(function(i) {
  33. self.set(i[0], i[1]);
  34. });
  35. }
  36. }
  37. /**
  38. * @param {Object} key
  39. * @return {Array} An array of values, undefined if no such a key;
  40. */
  41. Multimap.prototype.get = function(key) {
  42. return this._map ? this._.get(key) : this._[key];
  43. };
  44. /**
  45. * @param {Object} key
  46. * @param {Object} val...
  47. */
  48. Multimap.prototype.set = function(key, val) {
  49. var args = Array.prototype.slice.call(arguments);
  50. key = args.shift();
  51. var entry = this.get(key);
  52. if (!entry) {
  53. entry = [];
  54. if (this._map)
  55. this._.set(key, entry);
  56. else
  57. this._[key] = entry;
  58. }
  59. Array.prototype.push.apply(entry, args);
  60. return this;
  61. };
  62. /**
  63. * @param {Object} key
  64. * @param {Object=} val
  65. * @return {boolean} true if any thing changed
  66. */
  67. Multimap.prototype.delete = function(key, val) {
  68. if (!this.has(key))
  69. return false;
  70. if (arguments.length == 1) {
  71. this._map ? (this._.delete(key)) : (delete this._[key]);
  72. return true;
  73. } else {
  74. var entry = this.get(key);
  75. var idx = entry.indexOf(val);
  76. if (idx != -1) {
  77. entry.splice(idx, 1);
  78. return true;
  79. }
  80. }
  81. return false;
  82. };
  83. /**
  84. * @param {Object} key
  85. * @param {Object=} val
  86. * @return {boolean} whether the map contains 'key' or 'key=>val' pair
  87. */
  88. Multimap.prototype.has = function(key, val) {
  89. var hasKey = this._map ? this._.has(key) : this._.hasOwnProperty(key);
  90. if (arguments.length == 1 || !hasKey)
  91. return hasKey;
  92. var entry = this.get(key) || [];
  93. return entry.indexOf(val) != -1;
  94. };
  95. /**
  96. * @return {Array} all the keys in the map
  97. */
  98. Multimap.prototype.keys = function() {
  99. if (this._map)
  100. return makeIterator(this._.keys());
  101. return makeIterator(Object.keys(this._));
  102. };
  103. /**
  104. * @return {Array} all the values in the map
  105. */
  106. Multimap.prototype.values = function() {
  107. var vals = [];
  108. this.forEachEntry(function(entry) {
  109. Array.prototype.push.apply(vals, entry);
  110. });
  111. return makeIterator(vals);
  112. };
  113. /**
  114. *
  115. */
  116. Multimap.prototype.forEachEntry = function(iter) {
  117. mapEach(this, iter);
  118. };
  119. Multimap.prototype.forEach = function(iter) {
  120. var self = this;
  121. self.forEachEntry(function(entry, key) {
  122. entry.forEach(function(item) {
  123. iter(item, key, self);
  124. });
  125. });
  126. };
  127. Multimap.prototype.clear = function() {
  128. if (this._map) {
  129. this._.clear();
  130. } else {
  131. this._ = {};
  132. }
  133. };
  134. Object.defineProperty(
  135. Multimap.prototype,
  136. "size", {
  137. configurable: false,
  138. enumerable: true,
  139. get: function() {
  140. var total = 0;
  141. mapEach(this, function(value){
  142. total += value.length;
  143. });
  144. return total;
  145. }
  146. });
  147. Object.defineProperty(
  148. Multimap.prototype,
  149. "count", {
  150. configurable: false,
  151. enumerable: true,
  152. get: function() {
  153. return this._.size;
  154. }
  155. });
  156. var safariNext;
  157. try{
  158. safariNext = new Function('iterator', 'makeIterator', 'var keysArray = []; for(var key of iterator){keysArray.push(key);} return makeIterator(keysArray).next;');
  159. }catch(error){
  160. // for of not implemented;
  161. }
  162. function makeIterator(iterator){
  163. if(Array.isArray(iterator)){
  164. var nextIndex = 0;
  165. return {
  166. next: function(){
  167. return nextIndex < iterator.length ?
  168. {value: iterator[nextIndex++], done: false} :
  169. {done: true};
  170. }
  171. };
  172. }
  173. // Only an issue in safari
  174. if(!iterator.next && safariNext){
  175. iterator.next = safariNext(iterator, makeIterator);
  176. }
  177. return iterator;
  178. }
  179. return Multimap;
  180. })();
  181. if(typeof exports === 'object' && module && module.exports)
  182. module.exports = Multimap;
  183. else if(typeof define === 'function' && define.amd)
  184. define(function() { return Multimap; });