groupBy.js 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /** PURE_IMPORTS_START .._Subscriber,.._Subscription,.._Observable,.._Subject,.._util_Map,.._util_FastMap PURE_IMPORTS_END */
  2. var __extends = (this && this.__extends) || function (d, b) {
  3. for (var p in b)
  4. if (b.hasOwnProperty(p))
  5. d[p] = b[p];
  6. function __() { this.constructor = d; }
  7. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  8. };
  9. import { Subscriber } from '../Subscriber';
  10. import { Subscription } from '../Subscription';
  11. import { Observable } from '../Observable';
  12. import { Subject } from '../Subject';
  13. import { Map } from '../util/Map';
  14. import { FastMap } from '../util/FastMap';
  15. /* tslint:enable:max-line-length */
  16. /**
  17. * Groups the items emitted by an Observable according to a specified criterion,
  18. * and emits these grouped items as `GroupedObservables`, one
  19. * {@link GroupedObservable} per group.
  20. *
  21. * <img src="./img/groupBy.png" width="100%">
  22. *
  23. * @example <caption>Group objects by id and return as array</caption>
  24. * Observable.of<Obj>({id: 1, name: 'aze1'},
  25. * {id: 2, name: 'sf2'},
  26. * {id: 2, name: 'dg2'},
  27. * {id: 1, name: 'erg1'},
  28. * {id: 1, name: 'df1'},
  29. * {id: 2, name: 'sfqfb2'},
  30. * {id: 3, name: 'qfs3'},
  31. * {id: 2, name: 'qsgqsfg2'}
  32. * )
  33. * .groupBy(p => p.id)
  34. * .flatMap( (group$) => group$.reduce((acc, cur) => [...acc, cur], []))
  35. * .subscribe(p => console.log(p));
  36. *
  37. * // displays:
  38. * // [ { id: 1, name: 'aze1' },
  39. * // { id: 1, name: 'erg1' },
  40. * // { id: 1, name: 'df1' } ]
  41. * //
  42. * // [ { id: 2, name: 'sf2' },
  43. * // { id: 2, name: 'dg2' },
  44. * // { id: 2, name: 'sfqfb2' },
  45. * // { id: 2, name: 'qsgqsfg2' } ]
  46. * //
  47. * // [ { id: 3, name: 'qfs3' } ]
  48. *
  49. * @example <caption>Pivot data on the id field</caption>
  50. * Observable.of<Obj>({id: 1, name: 'aze1'},
  51. * {id: 2, name: 'sf2'},
  52. * {id: 2, name: 'dg2'},
  53. * {id: 1, name: 'erg1'},
  54. * {id: 1, name: 'df1'},
  55. * {id: 2, name: 'sfqfb2'},
  56. * {id: 3, name: 'qfs1'},
  57. * {id: 2, name: 'qsgqsfg2'}
  58. * )
  59. * .groupBy(p => p.id, p => p.name)
  60. * .flatMap( (group$) => group$.reduce((acc, cur) => [...acc, cur], ["" + group$.key]))
  61. * .map(arr => ({'id': parseInt(arr[0]), 'values': arr.slice(1)}))
  62. * .subscribe(p => console.log(p));
  63. *
  64. * // displays:
  65. * // { id: 1, values: [ 'aze1', 'erg1', 'df1' ] }
  66. * // { id: 2, values: [ 'sf2', 'dg2', 'sfqfb2', 'qsgqsfg2' ] }
  67. * // { id: 3, values: [ 'qfs1' ] }
  68. *
  69. * @param {function(value: T): K} keySelector A function that extracts the key
  70. * for each item.
  71. * @param {function(value: T): R} [elementSelector] A function that extracts the
  72. * return element for each item.
  73. * @param {function(grouped: GroupedObservable<K,R>): Observable<any>} [durationSelector]
  74. * A function that returns an Observable to determine how long each group should
  75. * exist.
  76. * @return {Observable<GroupedObservable<K,R>>} An Observable that emits
  77. * GroupedObservables, each of which corresponds to a unique key value and each
  78. * of which emits those items from the source Observable that share that key
  79. * value.
  80. * @method groupBy
  81. * @owner Observable
  82. */
  83. export function groupBy(keySelector, elementSelector, durationSelector, subjectSelector) {
  84. return function (source) {
  85. return source.lift(new GroupByOperator(keySelector, elementSelector, durationSelector, subjectSelector));
  86. };
  87. }
  88. var GroupByOperator = /*@__PURE__*/ (/*@__PURE__*/ function () {
  89. function GroupByOperator(keySelector, elementSelector, durationSelector, subjectSelector) {
  90. this.keySelector = keySelector;
  91. this.elementSelector = elementSelector;
  92. this.durationSelector = durationSelector;
  93. this.subjectSelector = subjectSelector;
  94. }
  95. GroupByOperator.prototype.call = function (subscriber, source) {
  96. return source.subscribe(new GroupBySubscriber(subscriber, this.keySelector, this.elementSelector, this.durationSelector, this.subjectSelector));
  97. };
  98. return GroupByOperator;
  99. }());
  100. /**
  101. * We need this JSDoc comment for affecting ESDoc.
  102. * @ignore
  103. * @extends {Ignored}
  104. */
  105. var GroupBySubscriber = /*@__PURE__*/ (/*@__PURE__*/ function (_super) {
  106. __extends(GroupBySubscriber, _super);
  107. function GroupBySubscriber(destination, keySelector, elementSelector, durationSelector, subjectSelector) {
  108. _super.call(this, destination);
  109. this.keySelector = keySelector;
  110. this.elementSelector = elementSelector;
  111. this.durationSelector = durationSelector;
  112. this.subjectSelector = subjectSelector;
  113. this.groups = null;
  114. this.attemptedToUnsubscribe = false;
  115. this.count = 0;
  116. }
  117. GroupBySubscriber.prototype._next = function (value) {
  118. var key;
  119. try {
  120. key = this.keySelector(value);
  121. }
  122. catch (err) {
  123. this.error(err);
  124. return;
  125. }
  126. this._group(value, key);
  127. };
  128. GroupBySubscriber.prototype._group = function (value, key) {
  129. var groups = this.groups;
  130. if (!groups) {
  131. groups = this.groups = typeof key === 'string' ? new FastMap() : new Map();
  132. }
  133. var group = groups.get(key);
  134. var element;
  135. if (this.elementSelector) {
  136. try {
  137. element = this.elementSelector(value);
  138. }
  139. catch (err) {
  140. this.error(err);
  141. }
  142. }
  143. else {
  144. element = value;
  145. }
  146. if (!group) {
  147. group = this.subjectSelector ? this.subjectSelector() : new Subject();
  148. groups.set(key, group);
  149. var groupedObservable = new GroupedObservable(key, group, this);
  150. this.destination.next(groupedObservable);
  151. if (this.durationSelector) {
  152. var duration = void 0;
  153. try {
  154. duration = this.durationSelector(new GroupedObservable(key, group));
  155. }
  156. catch (err) {
  157. this.error(err);
  158. return;
  159. }
  160. this.add(duration.subscribe(new GroupDurationSubscriber(key, group, this)));
  161. }
  162. }
  163. if (!group.closed) {
  164. group.next(element);
  165. }
  166. };
  167. GroupBySubscriber.prototype._error = function (err) {
  168. var groups = this.groups;
  169. if (groups) {
  170. groups.forEach(function (group, key) {
  171. group.error(err);
  172. });
  173. groups.clear();
  174. }
  175. this.destination.error(err);
  176. };
  177. GroupBySubscriber.prototype._complete = function () {
  178. var groups = this.groups;
  179. if (groups) {
  180. groups.forEach(function (group, key) {
  181. group.complete();
  182. });
  183. groups.clear();
  184. }
  185. this.destination.complete();
  186. };
  187. GroupBySubscriber.prototype.removeGroup = function (key) {
  188. this.groups.delete(key);
  189. };
  190. GroupBySubscriber.prototype.unsubscribe = function () {
  191. if (!this.closed) {
  192. this.attemptedToUnsubscribe = true;
  193. if (this.count === 0) {
  194. _super.prototype.unsubscribe.call(this);
  195. }
  196. }
  197. };
  198. return GroupBySubscriber;
  199. }(Subscriber));
  200. /**
  201. * We need this JSDoc comment for affecting ESDoc.
  202. * @ignore
  203. * @extends {Ignored}
  204. */
  205. var GroupDurationSubscriber = /*@__PURE__*/ (/*@__PURE__*/ function (_super) {
  206. __extends(GroupDurationSubscriber, _super);
  207. function GroupDurationSubscriber(key, group, parent) {
  208. _super.call(this, group);
  209. this.key = key;
  210. this.group = group;
  211. this.parent = parent;
  212. }
  213. GroupDurationSubscriber.prototype._next = function (value) {
  214. this.complete();
  215. };
  216. /** @deprecated internal use only */ GroupDurationSubscriber.prototype._unsubscribe = function () {
  217. var _a = this, parent = _a.parent, key = _a.key;
  218. this.key = this.parent = null;
  219. if (parent) {
  220. parent.removeGroup(key);
  221. }
  222. };
  223. return GroupDurationSubscriber;
  224. }(Subscriber));
  225. /**
  226. * An Observable representing values belonging to the same group represented by
  227. * a common key. The values emitted by a GroupedObservable come from the source
  228. * Observable. The common key is available as the field `key` on a
  229. * GroupedObservable instance.
  230. *
  231. * @class GroupedObservable<K, T>
  232. */
  233. export var GroupedObservable = /*@__PURE__*/ (/*@__PURE__*/ function (_super) {
  234. __extends(GroupedObservable, _super);
  235. function GroupedObservable(key, groupSubject, refCountSubscription) {
  236. _super.call(this);
  237. this.key = key;
  238. this.groupSubject = groupSubject;
  239. this.refCountSubscription = refCountSubscription;
  240. }
  241. /** @deprecated internal use only */ GroupedObservable.prototype._subscribe = function (subscriber) {
  242. var subscription = new Subscription();
  243. var _a = this, refCountSubscription = _a.refCountSubscription, groupSubject = _a.groupSubject;
  244. if (refCountSubscription && !refCountSubscription.closed) {
  245. subscription.add(new InnerRefCountSubscription(refCountSubscription));
  246. }
  247. subscription.add(groupSubject.subscribe(subscriber));
  248. return subscription;
  249. };
  250. return GroupedObservable;
  251. }(Observable));
  252. /**
  253. * We need this JSDoc comment for affecting ESDoc.
  254. * @ignore
  255. * @extends {Ignored}
  256. */
  257. var InnerRefCountSubscription = /*@__PURE__*/ (/*@__PURE__*/ function (_super) {
  258. __extends(InnerRefCountSubscription, _super);
  259. function InnerRefCountSubscription(parent) {
  260. _super.call(this);
  261. this.parent = parent;
  262. parent.count++;
  263. }
  264. InnerRefCountSubscription.prototype.unsubscribe = function () {
  265. var parent = this.parent;
  266. if (!parent.closed && !this.closed) {
  267. _super.prototype.unsubscribe.call(this);
  268. parent.count -= 1;
  269. if (parent.count === 0 && parent.attemptedToUnsubscribe) {
  270. parent.unsubscribe();
  271. }
  272. }
  273. };
  274. return InnerRefCountSubscription;
  275. }(Subscription));
  276. //# sourceMappingURL=groupBy.js.map