From 049817bdfe3c73c76a504104d84b039406741db8 Mon Sep 17 00:00:00 2001 From: Ben Lesh Date: Wed, 22 Jan 2020 17:45:29 -0600 Subject: [PATCH] feat(zipWith): add zipWith which is just a rename of zip (#5249) * feat(zipWith): add zipWith which is just a rename of zip - moves tests around to split off legacy behaviors * chore(zipWith): update tests for run mode * chore: fix alignments --- spec/operators/zip-legacy-spec.ts | 173 +++++++++ spec/operators/zip-spec.ts | 617 ------------------------------ spec/operators/zipWith-spec.ts | 559 +++++++++++++++++++++++++++ src/internal/operators/index.ts | 2 +- src/internal/operators/zip.ts | 46 --- src/internal/operators/zipWith.ts | 78 ++++ src/operators/index.ts | 2 +- 7 files changed, 812 insertions(+), 665 deletions(-) create mode 100644 spec/operators/zip-legacy-spec.ts delete mode 100644 spec/operators/zip-spec.ts create mode 100644 spec/operators/zipWith-spec.ts delete mode 100644 src/internal/operators/zip.ts create mode 100644 src/internal/operators/zipWith.ts diff --git a/spec/operators/zip-legacy-spec.ts b/spec/operators/zip-legacy-spec.ts new file mode 100644 index 00000000000..16c4a378433 --- /dev/null +++ b/spec/operators/zip-legacy-spec.ts @@ -0,0 +1,173 @@ +import { expect } from 'chai'; +import { zip } from 'rxjs/operators'; +import { from } from 'rxjs'; +import { TestScheduler } from 'rxjs/testing'; +import { observableMatcher } from '../helpers/observableMatcher'; + +/** + * zip legacy still supports a mapping function, but it's deprecated + */ +describe('zip legacy', () => { + let rxTestScheduler: TestScheduler; + + beforeEach(() => { + rxTestScheduler = new TestScheduler(observableMatcher); + }); + + it('should zip the provided observables', done => { + const expected = ['a1', 'b2', 'c3']; + let i = 0; + + from(['a', 'b', 'c']) + .pipe(zip(from([1, 2, 3]), (a, b): string => a + b)) + .subscribe( + function(x) { + expect(x).to.equal(expected[i++]); + }, + null, + done + ); + }); + + it('should work with selector throws', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot('---1-^-2---4----| '); + const asubs = ' ^-------! '; + const b = hot('---1-^--3----5----|'); + const bsubs = ' ^-------! '; + const expected = ' ---x----# '; + + const selector = function(x: string, y: string) { + if (y === '5') { + throw new Error('too bad'); + } else { + return x + y; + } + }; + const observable = a.pipe(zip(b, selector)); + expectObservable(observable).toBe(expected, { x: '23' }, new Error('too bad')); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with some data asymmetric 1', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot('---1-^-1-3-5-7-9-x-y-z-w-u-|'); + const asubs = ' ^-----------------! '; + const b = hot('---1-^--2--4--6--8--0--| '); + const bsubs = ' ^-----------------! '; + const expected = ' ---a--b--c--d--e--| '; + + expectObservable( + a.pipe( + zip(b, function(r1, r2) { + return r1 + r2; + }) + ) + ).toBe(expected, { a: '12', b: '34', c: '56', d: '78', e: '90' }); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with some data asymmetric 2', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot('---1-^--2--4--6--8--0--| '); + const asubs = ' ^-----------------! '; + const b = hot('---1-^-1-3-5-7-9-x-y-z-w-u-|'); + const bsubs = ' ^-----------------! '; + const expected = ' ---a--b--c--d--e--| '; + + expectObservable( + a.pipe( + zip(b, function(r1, r2) { + return r1 + r2; + }) + ) + ).toBe(expected, { a: '21', b: '43', c: '65', d: '87', e: '09' }); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with some data symmetric', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot('---1-^-1-3-5-7-9------| '); + const asubs = ' ^----------------! '; + const b = hot('---1-^--2--4--6--8--0--|'); + const bsubs = ' ^----------------! '; + const expected = ' ---a--b--c--d--e-| '; + + expectObservable( + a.pipe( + zip(b, function(r1, r2) { + return r1 + r2; + }) + ) + ).toBe(expected, { a: '12', b: '34', c: '56', d: '78', e: '90' }); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with n-ary symmetric selector', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot('---1-^-1----4----|'); + const asubs = ' ^---------! '; + const b = hot('---1-^--2--5----| '); + const bsubs = ' ^---------! '; + const c = hot('---1-^---3---6-| '); + const expected = ' ----x---y-| '; + + const observable = a.pipe( + zip(b, c, function(r0, r1, r2) { + return [r0, r1, r2]; + }) + ); + expectObservable(observable).toBe(expected, { x: ['1', '2', '3'], y: ['4', '5', '6'] }); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with n-ary symmetric array selector', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot('---1-^-1----4----|'); + const asubs = ' ^---------! '; + const b = hot('---1-^--2--5----| '); + const bsubs = ' ^---------! '; + const c = hot('---1-^---3---6-| '); + const expected = ' ----x---y-| '; + + const observable = a.pipe( + zip(b, c, function(r0, r1, r2) { + return [r0, r1, r2]; + }) + ); + expectObservable(observable).toBe(expected, { x: ['1', '2', '3'], y: ['4', '5', '6'] }); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should combine two observables and selector', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot(' ---1---2---3---'); + const asubs = ' ^'; + const b = hot(' --4--5--6--7--8--'); + const bsubs = ' ^'; + const expected = '---x---y---z'; + + expectObservable( + a.pipe( + zip(b, function(e1, e2) { + return e1 + e2; + }) + ) + ).toBe(expected, { x: '14', y: '25', z: '36' }); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); +}); diff --git a/spec/operators/zip-spec.ts b/spec/operators/zip-spec.ts deleted file mode 100644 index 1131409366c..00000000000 --- a/spec/operators/zip-spec.ts +++ /dev/null @@ -1,617 +0,0 @@ -import { expect } from 'chai'; -import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; -import { zip, mergeMap } from 'rxjs/operators'; -import { queueScheduler, from, of, Observable } from 'rxjs'; - -declare const type: Function; - -/** @test {zip} */ -describe('zip operator', () => { - it('should combine a source with a second', () => { - const a = hot('---1---2---3---'); - const asubs = '^'; - const b = hot('--4--5--6--7--8--'); - const bsubs = '^'; - const expected = '---x---y---z'; - - expectObservable(a.pipe(zip(b))) - .toBe(expected, { x: ['1', '4'], y: ['2', '5'], z: ['3', '6'] }); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should zip the provided observables', (done) => { - const expected = ['a1', 'b2', 'c3']; - let i = 0; - - from(['a', 'b', 'c']).pipe(zip( - from([1, 2, 3]), - (a, b): string => a + b - )) - .subscribe(function (x) { - expect(x).to.equal(expected[i++]); - }, null, done); - }); - - it('should end once one observable completes and its buffer is empty', () => { - const e1 = hot('---a--b--c--| '); - const e1subs = '^ ! '; - const e2 = hot('------d----e----f--------| '); - const e2subs = '^ ! '; - const e3 = hot('--------h----i----j---------'); // doesn't complete - const e3subs = '^ ! '; - const expected = '--------x----y----(z|) '; // e1 complete and buffer empty - const values = { - x: ['a', 'd', 'h'], - y: ['b', 'e', 'i'], - z: ['c', 'f', 'j'] - }; - - expectObservable(e1.pipe(zip(e2, e3))).toBe(expected, values); - expectSubscriptions(e1.subscriptions).toBe(e1subs); - expectSubscriptions(e2.subscriptions).toBe(e2subs); - expectSubscriptions(e3.subscriptions).toBe(e3subs); - }); - - it('should end once one observable nexts and zips value from completed other ' + - 'observable whose buffer is empty', () => { - const e1 = hot('---a--b--c--| '); - const e1subs = '^ ! '; - const e2 = hot('------d----e----f| '); - const e2subs = '^ ! '; - const e3 = hot('--------h----i----j-------'); // doesn't complete - const e3subs = '^ ! '; - const expected = '--------x----y----(z|) '; // e2 buffer empty and signaled complete - const values = { - x: ['a', 'd', 'h'], - y: ['b', 'e', 'i'], - z: ['c', 'f', 'j'] - }; - - expectObservable(e1.pipe(zip(e2, e3))).toBe(expected, values); - expectSubscriptions(e1.subscriptions).toBe(e1subs); - expectSubscriptions(e2.subscriptions).toBe(e2subs); - expectSubscriptions(e3.subscriptions).toBe(e3subs); - }); - - describe('with iterables', () => { - it('should zip them with values', () => { - const myIterator = { - count: 0, - next: function () { - return { value: this.count++, done: false }; - } - }; - myIterator[Symbol.iterator] = function () { return this; }; - - const e1 = hot('---a---b---c---d---|'); - const e1subs = '^ !'; - const expected = '---w---x---y---z---|'; - - const values = { - w: ['a', 0], - x: ['b', 1], - y: ['c', 2], - z: ['d', 3] - }; - - expectObservable(e1.pipe(zip(myIterator))).toBe(expected, values); - expectSubscriptions(e1.subscriptions).toBe(e1subs); - }); - - it('should only call `next` as needed', () => { - let nextCalled = 0; - const myIterator = { - count: 0, - next: function () { - nextCalled++; - return { value: this.count++, done: false }; - } - }; - myIterator[Symbol.iterator] = function () { return this; }; - - of(1, 2, 3).pipe(zip(myIterator)) - .subscribe(); - - // since zip will call `next()` in advance, total calls when - // zipped with 3 other values should be 4. - expect(nextCalled).to.equal(4); - }); - - it('should work with never observable and empty iterable', () => { - const a = cold( '-'); - const asubs = '^'; - const b: string[] = []; - const expected = '-'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - }); - - it('should work with empty observable and empty iterable', () => { - const a = cold('|'); - const asubs = '(^!)'; - const b: string[] = []; - const expected = '|'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - }); - - it('should work with empty observable and non-empty iterable', () => { - const a = cold('|'); - const asubs = '(^!)'; - const b = [1]; - const expected = '|'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - }); - - it('should work with non-empty observable and empty iterable', () => { - const a = hot('---^----a--|'); - const asubs = '^ !'; - const b: string[] = []; - const expected = '--------|'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - }); - - it('should work with never observable and non-empty iterable', () => { - const a = cold('-'); - const asubs = '^'; - const b = [1]; - const expected = '-'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - }); - - it('should work with non-empty observable and non-empty iterable', () => { - const a = hot('---^----1--|'); - const asubs = '^ ! '; - const b = [2]; - const expected = '-----(x|)'; - - expectObservable(a.pipe(zip(b))).toBe(expected, { x: ['1', 2] }); - expectSubscriptions(a.subscriptions).toBe(asubs); - }); - - it('should work with non-empty observable and empty iterable', () => { - const a = hot('---^----#'); - const asubs = '^ !'; - const b: string[] = []; - const expected = '-----#'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - }); - - it('should work with observable which raises error and non-empty iterable', () => { - const a = hot('---^----#'); - const asubs = '^ !'; - const b = [1]; - const expected = '-----#'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - }); - - it('should work with non-empty many observable and non-empty many iterable', () => { - const a = hot('---^--1--2--3--|'); - const asubs = '^ ! '; - const b = [4, 5, 6]; - const expected = '---x--y--(z|)'; - - expectObservable(a.pipe(zip(b))).toBe(expected, - { x: ['1', 4], y: ['2', 5], z: ['3', 6] }); - expectSubscriptions(a.subscriptions).toBe(asubs); - }); - - it('should work with non-empty observable and non-empty iterable selector that throws', () => { - const a = hot('---^--1--2--3--|'); - const asubs = '^ !'; - const b = [4, 5, 6]; - const expected = '---x--#'; - - const selector = function (x: string, y: number) { - if (y === 5) { - throw new Error('too bad'); - } else { - return x + y; - }}; - expectObservable(a.pipe(zip(b, selector))).toBe(expected, - { x: '14' }, new Error('too bad')); - expectSubscriptions(a.subscriptions).toBe(asubs); - }); - }); - - it('should combine two observables and selector', () => { - const a = hot('---1---2---3---'); - const asubs = '^'; - const b = hot('--4--5--6--7--8--'); - const bsubs = '^'; - const expected = '---x---y---z'; - - expectObservable(a.pipe(zip(b, function (e1, e2) { return e1 + e2; }))) - .toBe(expected, { x: '14', y: '25', z: '36' }); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with n-ary symmetric', () => { - const a = hot('---1-^-1----4----|'); - const asubs = '^ ! '; - const b = hot('---1-^--2--5----| '); - const bsubs = '^ ! '; - const c = hot('---1-^---3---6-| '); - const expected = '----x---y-| '; - - expectObservable(a.pipe(zip(b, c))).toBe(expected, - { x: ['1', '2', '3'], y: ['4', '5', '6'] }); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with n-ary symmetric selector', () => { - const a = hot('---1-^-1----4----|'); - const asubs = '^ ! '; - const b = hot('---1-^--2--5----| '); - const bsubs = '^ ! '; - const c = hot('---1-^---3---6-| '); - const expected = '----x---y-| '; - - const observable = a.pipe(zip(b, c, - function (r0, r1, r2) { return [r0, r1, r2]; })); - expectObservable(observable).toBe(expected, - { x: ['1', '2', '3'], y: ['4', '5', '6'] }); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with n-ary symmetric array selector', () => { - const a = hot('---1-^-1----4----|'); - const asubs = '^ ! '; - const b = hot('---1-^--2--5----| '); - const bsubs = '^ ! '; - const c = hot('---1-^---3---6-| '); - const expected = '----x---y-| '; - - const observable = a.pipe(zip(b, c, - function (r0, r1, r2) { return [r0, r1, r2]; })); - expectObservable(observable).toBe(expected, - { x: ['1', '2', '3'], y: ['4', '5', '6'] }); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with some data asymmetric 1', () => { - const a = hot('---1-^-1-3-5-7-9-x-y-z-w-u-|'); - const asubs = '^ ! '; - const b = hot('---1-^--2--4--6--8--0--| '); - const bsubs = '^ ! '; - const expected = '---a--b--c--d--e--| '; - - expectObservable(a.pipe(zip(b, function (r1, r2) { return r1 + r2; }))) - .toBe(expected, { a: '12', b: '34', c: '56', d: '78', e: '90' }); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with some data asymmetric 2', () => { - const a = hot('---1-^--2--4--6--8--0--| '); - const asubs = '^ ! '; - const b = hot('---1-^-1-3-5-7-9-x-y-z-w-u-|'); - const bsubs = '^ ! '; - const expected = '---a--b--c--d--e--| '; - - expectObservable(a.pipe(zip(b, function (r1, r2) { return r1 + r2; }))) - .toBe(expected, { a: '21', b: '43', c: '65', d: '87', e: '09' }); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with some data symmetric', () => { - const a = hot('---1-^-1-3-5-7-9------| '); - const asubs = '^ ! '; - const b = hot('---1-^--2--4--6--8--0--|'); - const bsubs = '^ ! '; - const expected = '---a--b--c--d--e-| '; - - expectObservable(a.pipe(zip(b, function (r1, r2) { return r1 + r2; }))) - .toBe(expected, { a: '12', b: '34', c: '56', d: '78', e: '90' }); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with selector throws', () => { - const a = hot('---1-^-2---4----| '); - const asubs = '^ ! '; - const b = hot('---1-^--3----5----|'); - const bsubs = '^ ! '; - const expected = '---x----# '; - - const selector = function (x: string, y: string) { - if (y === '5') { - throw new Error('too bad'); - } else { - return x + y; - }}; - const observable = a.pipe(zip(b, selector)); - expectObservable(observable).toBe(expected, - { x: '23' }, new Error('too bad')); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with right completes first', () => { - const a = hot('---1-^-2-----|'); - const asubs = '^ !'; - const b = hot('---1-^--3--|'); - const bsubs = '^ !'; - const expected = '---x--|'; - - expectObservable(a.pipe(zip(b))).toBe(expected, { x: ['2', '3'] }); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with two nevers', () => { - const a = cold( '-'); - const asubs = '^'; - const b = cold( '-'); - const bsubs = '^'; - const expected = '-'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with never and empty', () => { - const a = cold( '-'); - const asubs = '(^!)'; - const b = cold( '|'); - const bsubs = '(^!)'; - const expected = '|'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with empty and never', () => { - const a = cold( '|'); - const asubs = '(^!)'; - const b = cold( '-'); - const bsubs = '(^!)'; - const expected = '|'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with empty and empty', () => { - const a = cold( '|'); - const asubs = '(^!)'; - const b = cold( '|'); - const bsubs = '(^!)'; - const expected = '|'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with empty and non-empty', () => { - const a = cold( '|'); - const asubs = '(^!)'; - const b = hot( '---1--|'); - const bsubs = '(^!)'; - const expected = '|'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with non-empty and empty', () => { - const a = hot( '---1--|'); - const asubs = '(^!)'; - const b = cold( '|'); - const bsubs = '(^!)'; - const expected = '|'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with never and non-empty', () => { - const a = cold( '-'); - const asubs = '^'; - const b = hot( '---1--|'); - const bsubs = '^ !'; - const expected = '-'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with non-empty and never', () => { - const a = hot( '---1--|'); - const asubs = '^ !'; - const b = cold( '-'); - const bsubs = '^'; - const expected = '-'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with empty and error', () => { - const a = cold( '|'); - const asubs = '(^!)'; - const b = hot( '------#', undefined, 'too bad'); - const bsubs = '(^!)'; - const expected = '|'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with error and empty', () => { - const a = hot( '------#', undefined, 'too bad'); - const asubs = '(^!)'; - const b = cold( '|'); - const bsubs = '(^!)'; - const expected = '|'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with error', () => { - const a = hot('----------|'); - const asubs = '^ ! '; - const b = hot('------# '); - const bsubs = '^ ! '; - const expected = '------# '; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with never and error', () => { - const a = cold( '-'); - const asubs = '^ !'; - const b = hot('------#'); - const bsubs = '^ !'; - const expected = '------#'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with error and never', () => { - const a = hot('------#'); - const asubs = '^ !'; - const b = cold( '-'); - const bsubs = '^ !'; - const expected = '------#'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with error and error', () => { - const a = hot('------#', undefined, 'too bad'); - const asubs = '^ !'; - const b = hot('----------#', undefined, 'too bad 2'); - const bsubs = '^ !'; - const expected = '------#'; - - expectObservable(a.pipe(zip(b))).toBe(expected, null, 'too bad'); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with two sources that eventually raise errors', () => { - const a = hot('--w-----#----', { w: 1 }, 'too bad'); - const asubs = '^ !'; - const b = hot('-----z-----#-', { z: 2 }, 'too bad 2'); - const bsubs = '^ !'; - const expected = '-----x--#'; - - expectObservable(a.pipe(zip(b))).toBe(expected, { x: [1, 2] }, 'too bad'); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with two sources that eventually raise errors (swapped)', () => { - const a = hot('-----z-----#-', { z: 2 }, 'too bad 2'); - const asubs = '^ !'; - const b = hot('--w-----#----', { w: 1 }, 'too bad'); - const bsubs = '^ !'; - const expected = '-----x--#'; - - expectObservable(a.pipe(zip(b))).toBe(expected, { x: [2, 1] }, 'too bad'); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should work with error and some', () => { - const a = cold( '#'); - const asubs = '(^!)'; - const b = hot( '--1--2--3--'); - const bsubs = '(^!)'; - const expected = '#'; - - expectObservable(a.pipe(zip(b))).toBe(expected); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - it('should combine an immediately-scheduled source with an immediately-scheduled second', (done) => { - const a = of(1, 2, 3, queueScheduler); - const b = of(4, 5, 6, 7, 8, queueScheduler); - const r = [[1, 4], [2, 5], [3, 6]]; - let i = 0; - - a.pipe(zip(b)).subscribe(function (vals) { - expect(vals).to.deep.equal(r[i++]); - }, null, done); - }); - - it('should not break unsubscription chain when unsubscribed explicitly', () => { - const a = hot('---1---2---3---|'); - const unsub = ' !'; - const asubs = '^ !'; - const b = hot('--4--5--6--7--8--|'); - const bsubs = '^ !'; - const expected = '---x---y--'; - - const r = a.pipe( - mergeMap((x) => of(x)), - zip(b), - mergeMap((x) => of(x)) - ); - - expectObservable(r, unsub).toBe(expected, { x: ['1', '4'], y: ['2', '5']}); - expectSubscriptions(a.subscriptions).toBe(asubs); - expectSubscriptions(b.subscriptions).toBe(bsubs); - }); - - type('should support rest parameter observables', () => { - /* tslint:disable:no-unused-variable */ - let o: Observable; - let z: Observable[]; - let a: Observable = o!.pipe(zip(...z!)); - /* tslint:enable:no-unused-variable */ - }); - - type('should support projected rest parameter observables', () => { - /* tslint:disable:no-unused-variable */ - let o: Observable; - let z: Observable[]; - let a: Observable = o!.pipe(zip(...z!, (...r) => r.map(v => v.toString()))); - /* tslint:enable:no-unused-variable */ - }); - - type('should support projected arrays of observables', () => { - /* tslint:disable:no-unused-variable */ - let o: Observable; - let z: Observable[]; - let a: Observable = o!.pipe(zip(z!, (...r: any[]) => r.map(v => v.toString()))); - /* tslint:enable:no-unused-variable */ - }); -}); diff --git a/spec/operators/zipWith-spec.ts b/spec/operators/zipWith-spec.ts new file mode 100644 index 00000000000..a9c8344eb50 --- /dev/null +++ b/spec/operators/zipWith-spec.ts @@ -0,0 +1,559 @@ +import { expect } from 'chai'; +import { zipWith, mergeMap } from 'rxjs/operators'; +import { queueScheduler, of } from 'rxjs'; +import { TestScheduler } from 'rxjs/testing'; +import { observableMatcher } from '../helpers/observableMatcher'; +/** @test {zip} */ +describe('zipWith', () => { + let rxTestScheduler: TestScheduler; + + beforeEach(() => { + rxTestScheduler = new TestScheduler(observableMatcher); + }); + + it('should combine a source with a second', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot(' ---1---2---3---'); + const asubs = ' ^'; + const b = hot(' --4--5--6--7--8--'); + const bsubs = ' ^'; + const expected = '---x---y---z'; + expectObservable(a.pipe(zipWith(b))).toBe(expected, { x: ['1', '4'], y: ['2', '5'], z: ['3', '6'] }); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should end once one observable completes and its buffer is empty', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const e1 = hot(' ---a--b--c--| '); + const e1subs = ' ^-----------! '; + const e2 = hot(' ------d----e----f--------| '); + const e2subs = ' ^-----------------! '; + const e3 = hot(' --------h----i----j---------'); // doesn't complete + const e3subs = ' ^-----------------! '; + const expected = '--------x----y----(z|) '; // e1 complete and buffer empty + const values = { + x: ['a', 'd', 'h'], + y: ['b', 'e', 'i'], + z: ['c', 'f', 'j'], + }; + + expectObservable(e1.pipe(zipWith(e2, e3))).toBe(expected, values); + expectSubscriptions(e1.subscriptions).toBe(e1subs); + expectSubscriptions(e2.subscriptions).toBe(e2subs); + expectSubscriptions(e3.subscriptions).toBe(e3subs); + }); + }); + + it( + 'should end once one observable nexts and zips value from completed other ' + 'observable whose buffer is empty', + () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const e1 = hot(' ---a--b--c--| '); + const e1subs = ' ^-----------! '; + const e2 = hot(' ------d----e----f| '); + const e2subs = ' ^----------------! '; + const e3 = hot(' --------h----i----j-------'); // doesn't complete + const e3subs = ' ^-----------------! '; + const expected = '--------x----y----(z|) '; // e2 buffer empty and signaled complete + const values = { + x: ['a', 'd', 'h'], + y: ['b', 'e', 'i'], + z: ['c', 'f', 'j'], + }; + + expectObservable(e1.pipe(zipWith(e2, e3))).toBe(expected, values); + expectSubscriptions(e1.subscriptions).toBe(e1subs); + expectSubscriptions(e2.subscriptions).toBe(e2subs); + expectSubscriptions(e3.subscriptions).toBe(e3subs); + }); + } + ); + + describe('with iterables', () => { + it('should zip them with values', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const myIterator = { + count: 0, + next: function() { + return { value: this.count++, done: false }; + }, + }; + myIterator[Symbol.iterator] = function() { + return this; + }; + + const e1 = hot(' ---a---b---c---d---|'); + const e1subs = ' ^------------------!'; + const expected = '---w---x---y---z---|'; + + const values = { + w: ['a', 0], + x: ['b', 1], + y: ['c', 2], + z: ['d', 3], + }; + + expectObservable(e1.pipe(zipWith(myIterator))).toBe(expected, values); + expectSubscriptions(e1.subscriptions).toBe(e1subs); + }); + }); + + it('should only call `next` as needed', () => { + let nextCalled = 0; + const myIterator = { + count: 0, + next: function() { + nextCalled++; + return { value: this.count++, done: false }; + }, + }; + myIterator[Symbol.iterator] = function() { + return this; + }; + + of(1, 2, 3) + .pipe(zipWith(myIterator)) + .subscribe(); + + // since zip will call `next()` in advance, total calls when + // zipped with 3 other values should be 4. + expect(nextCalled).to.equal(4); + }); + + it('should work with never observable and empty iterable', () => { + rxTestScheduler.run(({ cold, expectObservable, expectSubscriptions }) => { + const a = cold(' -'); + const asubs = ' ^'; + const expected = '-'; + const b: string[] = []; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + }); + }); + + it('should work with empty observable and empty iterable', () => { + rxTestScheduler.run(({ cold, expectObservable, expectSubscriptions }) => { + const a = cold(' |'); + const asubs = ' (^!)'; + const expected = '|'; + const b: string[] = []; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + }); + }); + + it('should work with empty observable and non-empty iterable', () => { + rxTestScheduler.run(({ cold, expectObservable, expectSubscriptions }) => { + const a = cold(' |'); + const asubs = ' (^!)'; + const expected = '|'; + const b = [1]; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + }); + }); + + it('should work with non-empty observable and empty iterable', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot(' ---^----a--|'); + const asubs = ' ^-------!'; + const b: string[] = []; + const expected = '--------|'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + }); + }); + + it('should work with never observable and non-empty iterable', () => { + rxTestScheduler.run(({ cold, expectObservable, expectSubscriptions }) => { + const a = cold(' -'); + const asubs = ' ^'; + const expected = '-'; + const b = [1]; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + }); + }); + + it('should work with non-empty observable and non-empty iterable', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot('---^----1--|'); + const asubs = ' ^----! '; + const expected = '-----(x|)'; + const b = [2]; + + expectObservable(a.pipe(zipWith(b))).toBe(expected, { x: ['1', 2] }); + expectSubscriptions(a.subscriptions).toBe(asubs); + }); + }); + + it('should work with non-empty observable and empty iterable', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot('---^----#'); + const asubs = ' ^----!'; + const expected = '-----#'; + const b: string[] = []; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + }); + }); + + it('should work with observable which raises error and non-empty iterable', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot('---^----#'); + const asubs = ' ^----!'; + const expected = '-----#'; + const b = [1]; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + }); + }); + + it('should work with non-empty many observable and non-empty many iterable', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot('---^--1--2--3--|'); + const asubs = ' ^--------! '; + const expected = '---x--y--(z|)'; + const b = [4, 5, 6]; + + expectObservable(a.pipe(zipWith(b))).toBe(expected, { x: ['1', 4], y: ['2', 5], z: ['3', 6] }); + expectSubscriptions(a.subscriptions).toBe(asubs); + }); + }); + + it('should work with non-empty observable and non-empty iterable selector that throws', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot('---^--1--2--3--|'); + const asubs = ' ^-----!'; + const expected = '---x--#'; + const b = [4, 5, 6]; + + const selector = function(x: string, y: number) { + if (y === 5) { + throw new Error('too bad'); + } else { + return x + y; + } + }; + expectObservable(a.pipe(zipWith(b, selector))).toBe(expected, { x: '14' }, new Error('too bad')); + expectSubscriptions(a.subscriptions).toBe(asubs); + }); + }); + }); + + it('should work with n-ary symmetric', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot('---1-^-1----4----|'); + const asubs = ' ^---------! '; + const b = hot('---1-^--2--5----| '); + const bsubs = ' ^---------! '; + const c = hot('---1-^---3---6-| '); + const expected = ' ----x---y-| '; + + expectObservable(a.pipe(zipWith(b, c))).toBe(expected, { x: ['1', '2', '3'], y: ['4', '5', '6'] }); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with right completes first', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot('---1-^-2-----|'); + const asubs = ' ^-----!'; + const b = hot('---1-^--3--|'); + const bsubs = ' ^-----!'; + const expected = ' ---x--|'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected, { x: ['2', '3'] }); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with two nevers', () => { + rxTestScheduler.run(({ cold, expectObservable, expectSubscriptions }) => { + const a = cold(' -'); + const asubs = ' ^'; + const b = cold(' -'); + const bsubs = ' ^'; + const expected = '-'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with never and empty', () => { + rxTestScheduler.run(({ cold, expectObservable, expectSubscriptions }) => { + const a = cold(' -'); + const asubs = ' (^!)'; + const b = cold(' |'); + const bsubs = ' (^!)'; + const expected = '|'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with empty and never', () => { + rxTestScheduler.run(({ cold, expectObservable, expectSubscriptions }) => { + const a = cold(' |'); + const asubs = ' (^!)'; + const b = cold(' -'); + const bsubs = ' (^!)'; + const expected = '|'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with empty and empty', () => { + rxTestScheduler.run(({ cold, expectObservable, expectSubscriptions }) => { + const a = cold(' |'); + const asubs = ' (^!)'; + const b = cold(' |'); + const bsubs = ' (^!)'; + const expected = '|'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with empty and non-empty', () => { + rxTestScheduler.run(({ cold, hot, expectObservable, expectSubscriptions }) => { + const a = cold(' |'); + const asubs = ' (^!)'; + const b = hot(' ---1--|'); + const bsubs = ' (^!)'; + const expected = '|'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with non-empty and empty', () => { + rxTestScheduler.run(({ cold, hot, expectObservable, expectSubscriptions }) => { + const a = hot(' ---1--|'); + const asubs = ' (^!)'; + const b = cold(' |'); + const bsubs = ' (^!)'; + const expected = '|'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with never and non-empty', () => { + rxTestScheduler.run(({ cold, hot, expectObservable, expectSubscriptions }) => { + const a = cold(' -'); + const asubs = ' ^'; + const b = hot(' ---1--|'); + const bsubs = ' ^-----!'; + const expected = '-'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with non-empty and never', () => { + rxTestScheduler.run(({ cold, hot, expectObservable, expectSubscriptions }) => { + const a = hot(' ---1--|'); + const asubs = ' ^-----!'; + const b = cold(' -'); + const bsubs = ' ^'; + const expected = '-'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with empty and error', () => { + rxTestScheduler.run(({ cold, hot, expectObservable, expectSubscriptions }) => { + const a = cold(' |'); + const asubs = ' (^!)'; + const b = hot(' ------#', undefined, 'too bad'); + const bsubs = ' (^!)'; + const expected = '|'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with error and empty', () => { + rxTestScheduler.run(({ cold, hot, expectObservable, expectSubscriptions }) => { + const a = hot(' ------#', undefined, 'too bad'); + const asubs = ' (^!)'; + const b = cold(' |'); + const bsubs = ' (^!)'; + const expected = '|'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with error', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot(' ----------|'); + const asubs = ' ^-----! '; + const b = hot(' ------# '); + const bsubs = ' ^-----! '; + const expected = '------# '; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with never and error', () => { + rxTestScheduler.run(({ cold, hot, expectObservable, expectSubscriptions }) => { + const a = cold(' -------'); + const asubs = ' ^-----!'; + const b = hot(' ------#'); + const bsubs = ' ^-----!'; + const expected = '------#'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with error and never', () => { + rxTestScheduler.run(({ cold, hot, expectObservable, expectSubscriptions }) => { + const a = hot(' ------#'); + const asubs = ' ^-----!'; + const b = cold(' -------'); + const bsubs = ' ^-----!'; + const expected = '------#'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with error and error', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot(' ------#', undefined, 'too bad'); + const asubs = ' ^-----!'; + const b = hot(' ----------#', undefined, 'too bad 2'); + const bsubs = ' ^-----!'; + const expected = '------#'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected, null, 'too bad'); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with two sources that eventually raise errors', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot(' --w-----#----', { w: 1 }, 'too bad'); + const asubs = ' ^-------!'; + const b = hot(' -----z-----#-', { z: 2 }, 'too bad 2'); + const bsubs = ' ^-------!'; + const expected = '-----x--#'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected, { x: [1, 2] }, 'too bad'); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with two sources that eventually raise errors (swapped)', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot(' -----z-----#-', { z: 2 }, 'too bad 2'); + const asubs = ' ^-------!'; + const b = hot(' --w-----#----', { w: 1 }, 'too bad'); + const bsubs = ' ^-------!'; + const expected = '-----x--#'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected, { x: [2, 1] }, 'too bad'); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should work with error and some', () => { + rxTestScheduler.run(({ cold, hot, expectObservable, expectSubscriptions }) => { + const a = cold(' #'); + const asubs = ' (^!)'; + const b = hot(' --1--2--3--'); + const bsubs = ' (^!)'; + const expected = '#'; + + expectObservable(a.pipe(zipWith(b))).toBe(expected); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); + + it('should combine an immediately-scheduled source with an immediately-scheduled second', done => { + const a = of(1, 2, 3, queueScheduler); + const b = of(4, 5, 6, 7, 8, queueScheduler); + const r = [ + [1, 4], + [2, 5], + [3, 6], + ]; + let i = 0; + + a.pipe(zipWith(b)).subscribe( + function(vals) { + expect(vals).to.deep.equal(r[i++]); + }, + null, + done + ); + }); + + it('should not break unsubscription chain when unsubscribed explicitly', () => { + rxTestScheduler.run(({ hot, expectObservable, expectSubscriptions }) => { + const a = hot(' ---1---2---3---|'); + const unsub = ' ---------!'; + const asubs = ' ^--------!'; + const b = hot(' --4--5--6--7--8--|'); + const bsubs = ' ^--------!'; + const expected = '---x---y--'; + + const r = a.pipe( + mergeMap(x => of(x)), + zipWith(b), + mergeMap(x => of(x)) + ); + + expectObservable(r, unsub).toBe(expected, { x: ['1', '4'], y: ['2', '5'] }); + expectSubscriptions(a.subscriptions).toBe(asubs); + expectSubscriptions(b.subscriptions).toBe(bsubs); + }); + }); +}); diff --git a/src/internal/operators/index.ts b/src/internal/operators/index.ts index 0375852fd01..016cbb1b084 100644 --- a/src/internal/operators/index.ts +++ b/src/internal/operators/index.ts @@ -98,5 +98,5 @@ export { windowTime } from './windowTime'; export { windowToggle } from './windowToggle'; export { windowWhen } from './windowWhen'; export { withLatestFrom } from './withLatestFrom'; -export { zip } from './zip'; +export { zip, zipWith } from './zipWith'; export { zipAll } from './zipAll'; diff --git a/src/internal/operators/zip.ts b/src/internal/operators/zip.ts deleted file mode 100644 index 67060b08ab5..00000000000 --- a/src/internal/operators/zip.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { zip as zipStatic } from '../observable/zip'; -import { Observable } from '../Observable'; -import { ObservableInput, OperatorFunction } from '../types'; - -/* tslint:disable:max-line-length */ -/** @deprecated Deprecated in favor of static zip. */ -export function zip(project: (v1: T) => R): OperatorFunction; -/** @deprecated Deprecated in favor of static zip. */ -export function zip(v2: ObservableInput, project: (v1: T, v2: T2) => R): OperatorFunction; -/** @deprecated Deprecated in favor of static zip. */ -export function zip(v2: ObservableInput, v3: ObservableInput, project: (v1: T, v2: T2, v3: T3) => R): OperatorFunction; -/** @deprecated Deprecated in favor of static zip. */ -export function zip(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, project: (v1: T, v2: T2, v3: T3, v4: T4) => R): OperatorFunction; -/** @deprecated Deprecated in favor of static zip. */ -export function zip(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, project: (v1: T, v2: T2, v3: T3, v4: T4, v5: T5) => R): OperatorFunction; -/** @deprecated Deprecated in favor of static zip. */ -export function zip(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, v6: ObservableInput, project: (v1: T, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6) => R): OperatorFunction ; -/** @deprecated Deprecated in favor of static zip. */ -export function zip(v2: ObservableInput): OperatorFunction; -/** @deprecated Deprecated in favor of static zip. */ -export function zip(v2: ObservableInput, v3: ObservableInput): OperatorFunction; -/** @deprecated Deprecated in favor of static zip. */ -export function zip(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput): OperatorFunction; -/** @deprecated Deprecated in favor of static zip. */ -export function zip(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput): OperatorFunction; -/** @deprecated Deprecated in favor of static zip. */ -export function zip(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, v6: ObservableInput): OperatorFunction ; -/** @deprecated Deprecated in favor of static zip. */ -export function zip(...observables: Array | ((...values: Array) => R)>): OperatorFunction; -/** @deprecated Deprecated in favor of static zip. */ -export function zip(array: Array>): OperatorFunction; -/** @deprecated Deprecated in favor of static zip. */ -export function zip(array: Array>, project: (v1: T, ...values: Array) => R): OperatorFunction; -/* tslint:enable:max-line-length */ - -/** - * @deprecated Deprecated in favor of static {@link zip}. - */ -export function zip(...observables: Array | ((...values: Array) => R)>): OperatorFunction { - return function zipOperatorFunction(source: Observable) { - return source.lift.call( - zipStatic(source, ...observables), - undefined - ) as Observable; - }; -} \ No newline at end of file diff --git a/src/internal/operators/zipWith.ts b/src/internal/operators/zipWith.ts new file mode 100644 index 00000000000..e574271e6eb --- /dev/null +++ b/src/internal/operators/zipWith.ts @@ -0,0 +1,78 @@ +import { zip as zipStatic } from '../observable/zip'; +import { Observable } from '../Observable'; +import { ObservableInput, OperatorFunction, ObservedValuesFromArray } from '../types'; + +/* tslint:disable:max-line-length */ +/** @deprecated Deprecated use {@link zipWith} */ +export function zip(project: (v1: T) => R): OperatorFunction; +/** @deprecated Deprecated use {@link zipWith} */ +export function zip(v2: ObservableInput, project: (v1: T, v2: T2) => R): OperatorFunction; +/** @deprecated Deprecated use {@link zipWith} */ +export function zip(v2: ObservableInput, v3: ObservableInput, project: (v1: T, v2: T2, v3: T3) => R): OperatorFunction; +/** @deprecated Deprecated use {@link zipWith} */ +export function zip(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, project: (v1: T, v2: T2, v3: T3, v4: T4) => R): OperatorFunction; +/** @deprecated Deprecated use {@link zipWith} */ +export function zip(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, project: (v1: T, v2: T2, v3: T3, v4: T4, v5: T5) => R): OperatorFunction; +/** @deprecated Deprecated use {@link zipWith} */ +export function zip(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, v6: ObservableInput, project: (v1: T, v2: T2, v3: T3, v4: T4, v5: T5, v6: T6) => R): OperatorFunction ; +/** @deprecated Deprecated use {@link zipWith} */ +export function zip(v2: ObservableInput): OperatorFunction; +/** @deprecated Deprecated use {@link zipWith} */ +export function zip(v2: ObservableInput, v3: ObservableInput): OperatorFunction; +/** @deprecated Deprecated use {@link zipWith} */ +export function zip(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput): OperatorFunction; +/** @deprecated Deprecated use {@link zipWith} */ +export function zip(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput): OperatorFunction; +/** @deprecated Deprecated use {@link zipWith} */ +export function zip(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, v6: ObservableInput): OperatorFunction ; +/** @deprecated Deprecated use {@link zipWith} */ +export function zip(...observables: Array | ((...values: Array) => R)>): OperatorFunction; +/** @deprecated Deprecated use {@link zipWith} */ +export function zip(array: Array>): OperatorFunction; +/** @deprecated Deprecated use {@link zipWith} */ +export function zip(array: Array>, project: (v1: T, ...values: Array) => R): OperatorFunction; +/* tslint:enable:max-line-length */ + +/** + * @deprecated Deprecated. Use {@link zipWith}. + */ +export function zip(...observables: Array | ((...values: Array) => R)>): OperatorFunction { + return function zipOperatorFunction(source: Observable) { + return source.lift.call( + zipStatic(source, ...observables), + undefined + ) as Observable; + }; +} + +/* tslint:disable:max-line-length */ +export function zipWith(v2: ObservableInput): OperatorFunction; +export function zipWith(v2: ObservableInput, v3: ObservableInput): OperatorFunction; +export function zipWith(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput): OperatorFunction; +export function zipWith(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput): OperatorFunction; +export function zipWith(v2: ObservableInput, v3: ObservableInput, v4: ObservableInput, v5: ObservableInput, v6: ObservableInput): OperatorFunction ; +export function zipWith[]>(...otherInputs: A): OperatorFunction>>; +/* tslint:enable:max-line-length */ +/** + * Subscribes to the source, and the observable inputs provided as arguments, and combines their values, by index, into arrays. + * + * What is meant by "combine by index": The first value from each will be made into a single array, then emitted, + * then the second value from each will be combined into a single array and emitted, then the third value + * from each will be combined into a single array and emitted, and so on. + * + * This will continue until it is no longer able to combine values of the same index into an array. + * + * After the last value from any one completed source is emitted in an array, the resulting observable will complete, + * as there is no way to continue "zipping" values together by index. + * + * Use-cases for this operator are limited. There are memory concerns if one of the streams is emitting + * values at a much faster rate than the others. Usage should likely be limited to streams that emit + * at a similar pace, or finite streams of known length. + * + * In many cases, authors want `combineLatestWith` and not `zipWith`. + * + * @param otherInputs other observable inputs to collate values from. + */ +export function zipWith[]>(...otherInputs: A): OperatorFunction>> { + return zip(...otherInputs); +} diff --git a/src/operators/index.ts b/src/operators/index.ts index 49f9e78c270..b4c007bc7b8 100644 --- a/src/operators/index.ts +++ b/src/operators/index.ts @@ -103,5 +103,5 @@ export { windowTime } from '../internal/operators/windowTime'; export { windowToggle } from '../internal/operators/windowToggle'; export { windowWhen } from '../internal/operators/windowWhen'; export { withLatestFrom } from '../internal/operators/withLatestFrom'; -export { zip } from '../internal/operators/zip'; +export { zip, zipWith } from '../internal/operators/zipWith'; export { zipAll } from '../internal/operators/zipAll';